summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Config.php448
-rw-r--r--app/Contracts/ElementInterface.php2
-rw-r--r--app/Elements/AbstractElement.php4
-rw-r--r--app/Functions/FunctionsEdit.php911
-rw-r--r--app/GedcomRecord.php7
-rw-r--r--app/GedcomTag.php848
-rw-r--r--app/Http/RequestHandlers/AddChildToFamilyAction.php79
-rw-r--r--app/Http/RequestHandlers/AddChildToFamilyPage.php41
-rw-r--r--app/Http/RequestHandlers/AddChildToIndividualAction.php82
-rw-r--r--app/Http/RequestHandlers/AddChildToIndividualPage.php34
-rw-r--r--app/Http/RequestHandlers/AddName.php68
-rw-r--r--app/Http/RequestHandlers/AddNewFact.php27
-rw-r--r--app/Http/RequestHandlers/AddParentToIndividualAction.php59
-rw-r--r--app/Http/RequestHandlers/AddParentToIndividualPage.php43
-rw-r--r--app/Http/RequestHandlers/AddSpouseToFamilyAction.php74
-rw-r--r--app/Http/RequestHandlers/AddSpouseToFamilyPage.php47
-rw-r--r--app/Http/RequestHandlers/AddSpouseToIndividualAction.php70
-rw-r--r--app/Http/RequestHandlers/AddSpouseToIndividualPage.php59
-rw-r--r--app/Http/RequestHandlers/AddUnlinkedAction.php32
-rw-r--r--app/Http/RequestHandlers/AddUnlinkedPage.php29
-rw-r--r--app/Http/RequestHandlers/EditFactAction.php95
-rw-r--r--app/Http/RequestHandlers/EditName.php82
-rw-r--r--app/Http/RequestHandlers/LinkChildToFamilyAction.php4
-rw-r--r--app/Http/RequestHandlers/LinkChildToFamilyPage.php9
-rw-r--r--app/Http/RequestHandlers/LinkSpouseToIndividualAction.php17
-rw-r--r--app/Http/RequestHandlers/LinkSpouseToIndividualPage.php17
-rw-r--r--app/Http/RequestHandlers/ModulesCustomTagsPage.php2
-rw-r--r--app/Http/Routes/WebRoutes.php32
-rw-r--r--app/Module/CustomTagsAldfaer.php10
-rw-r--r--app/Module/CustomTagsAncestry.php10
-rw-r--r--app/Module/CustomTagsBrothersKeeper.php12
-rw-r--r--app/Module/CustomTagsFamilySearch.php10
-rw-r--r--app/Module/CustomTagsFamilyTreeBuilder.php10
-rw-r--r--app/Module/CustomTagsFamilyTreeMaker.php12
-rw-r--r--app/Module/CustomTagsGedcom53.php10
-rw-r--r--app/Module/CustomTagsGedcom55.php10
-rw-r--r--app/Module/CustomTagsGedcomL.php10
-rw-r--r--app/Module/CustomTagsLegacy.php10
-rw-r--r--app/Module/CustomTagsPersonalAncestralFile.php10
-rw-r--r--app/Module/CustomTagsPhpGedView.php10
-rw-r--r--app/Module/CustomTagsReunion.php10
-rw-r--r--app/Module/CustomTagsRootsMagic.php10
-rw-r--r--app/Module/CustomTagsWebtrees.php26
-rw-r--r--app/Module/ModuleCustomTagsTrait.php61
-rw-r--r--app/Services/GedcomService.php2
-rw-r--r--phpstan-baseline.neon5
-rw-r--r--resources/views/admin/control-panel.phtml2
-rw-r--r--resources/views/cards/add-associate.phtml26
-rw-r--r--resources/views/cards/add-fact.phtml22
-rw-r--r--resources/views/cards/add-media-object.phtml22
-rw-r--r--resources/views/cards/add-note.phtml22
-rw-r--r--resources/views/cards/add-restriction.phtml22
-rw-r--r--resources/views/cards/add-shared-note.phtml22
-rw-r--r--resources/views/cards/add-sour-data-even.phtml23
-rw-r--r--resources/views/cards/add-source-citation.phtml80
-rw-r--r--resources/views/edit/add-fact.phtml110
-rw-r--r--resources/views/edit/edit-fact.phtml83
-rw-r--r--resources/views/edit/link-spouse-to-individual.phtml40
-rw-r--r--resources/views/edit/new-individual.phtml494
-rw-r--r--resources/views/family-page-children.phtml4
-rw-r--r--resources/views/family-page-grandparents.phtml33
-rw-r--r--resources/views/family-page-menu.phtml6
-rw-r--r--resources/views/family-page-parents.phtml8
-rw-r--r--resources/views/individual-name.phtml15
-rw-r--r--resources/views/individual-page-menu.phtml3
-rw-r--r--resources/views/media-page-menu.phtml15
-rw-r--r--resources/views/media-page.phtml28
-rw-r--r--resources/views/modules/custom-tags/config.phtml43
-rw-r--r--resources/views/modules/relatives/family.phtml10
-rw-r--r--resources/views/modules/relatives/tab.phtml8
-rw-r--r--tests/.DS_Storebin0 -> 6148 bytes
71 files changed, 718 insertions, 3883 deletions
diff --git a/app/Config.php b/app/Config.php
deleted file mode 100644
index 3bffb3ff51..0000000000
--- a/app/Config.php
+++ /dev/null
@@ -1,448 +0,0 @@
-<?php
-
-/**
- * webtrees: online genealogy
- * Copyright (C) 2021 webtrees development team
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-declare(strict_types=1);
-
-namespace Fisharebest\Webtrees;
-
-/**
- * Application configuration data. Data here has no GUI to edit it,
- * although most of it can be altered to customise local installations.
- *
- * @deprecated since 2.0.7. Will be removed in 2.1.0
- */
-class Config
-{
- /**
- * NPFX tags - name prefixes
- *
- * @return array<string>
- */
- public static function namePrefixes(): array
- {
- return [
- 'Adm',
- 'Amb',
- 'Brig',
- 'Can',
- 'Capt',
- 'Chan',
- 'Chapln',
- 'Cmdr',
- 'Col',
- 'Cpl',
- 'Cpt',
- 'Dr',
- 'Gen',
- 'Gov',
- 'Hon',
- 'Lady',
- 'Lt',
- 'Mr',
- 'Mrs',
- 'Ms',
- 'Msgr',
- 'Pfc',
- 'Pres',
- 'Prof',
- 'Pvt',
- 'Rabbi',
- 'Rep',
- 'Rev',
- 'Sen',
- 'Sgt',
- 'Sir',
- 'Sr',
- 'Sra',
- 'Srta',
- 'Ven',
- ];
- }
-
- /**
- * FILE:FORM tags - file formats
- *
- * @return array<string>
- */
- public static function fileFormats(): array
- {
- return [
- 'avi',
- 'bmp',
- 'gif',
- 'jpeg',
- 'mp3',
- 'ole',
- 'pcx',
- 'png',
- 'tiff',
- 'wav',
- ];
- }
-
- /**
- * Facts and events that don't normally have a value
- *
- * @return array<string>
- */
- public static function emptyFacts(): array
- {
- return [
- 'ADOP',
- 'ANUL',
- 'BAPL',
- 'BAPM',
- 'BARM',
- 'BASM',
- 'BIRT',
- 'BLES',
- 'BURI',
- 'CENS',
- 'CHAN',
- 'CHR',
- 'CHRA',
- 'CONF',
- 'CONL',
- 'CREM',
- 'DATA',
- 'DEAT',
- 'DIV',
- 'DIVF',
- 'EMIG',
- 'ENDL',
- 'ENGA',
- 'FCOM',
- 'GRAD',
- 'HUSB',
- 'IMMI',
- 'MAP',
- 'MARB',
- 'MARC',
- 'MARL',
- 'MARR',
- 'MARS',
- 'NATU',
- 'ORDN',
- 'PROB',
- 'RESI',
- 'RETI',
- 'SLGC',
- 'SLGS',
- 'WIFE',
- 'WILL',
- '_HOL',
- '_NMR',
- '_NMAR',
- '_SEPR',
- ];
- }
-
- /**
- * Tags that don't require a PLAC subtag
- *
- * @return array<string>
- */
- public static function nonPlaceFacts(): array
- {
- return [
- 'ENDL',
- 'NCHI',
- 'REFN',
- 'SLGC',
- 'SLGS',
- ];
- }
-
- /**
- * Tags that don't require a DATE subtag
- *
- * @return array<string>
- */
- public static function nonDateFacts(): array
- {
- return [
- 'ABBR',
- 'ADDR',
- 'AFN',
- 'ALIA',
- 'AUTH',
- 'CHIL',
- 'EMAIL',
- 'FAX',
- 'FILE',
- 'HUSB',
- 'LANG',
- 'NAME',
- 'NCHI',
- 'NOTE',
- 'OBJE',
- 'PHON',
- 'PUBL',
- 'REFN',
- 'REPO',
- 'RESN',
- 'SEX',
- 'SOUR',
- 'SSN',
- 'TEXT',
- 'WIFE',
- 'WWW',
- '_EMAIL',
- ];
- }
-
- /**
- * Tags that require a DATE:TIME as well as a DATE
- *
- * @return array<string>
- */
- public static function dateAndTime(): array
- {
- return [
- 'BIRT',
- 'DEAT',
- ];
- }
-
- /**
- * Level 2 tags that apply to specific Level 1 tags
- * Tags are applied in the order they appear here.
- *
- * @return array<string,array<string>>
- */
- public static function levelTwoTags(): array
- {
- return [
- 'TYPE' => [
- 'EVEN',
- 'FACT',
- 'GRAD',
- 'IDNO',
- 'MARR',
- 'ORDN',
- 'SSN',
- ],
- 'AGNC' => [
- 'EDUC',
- 'GRAD',
- 'OCCU',
- 'ORDN',
- 'RETI',
- ],
- 'CALN' => [
- 'REPO',
- ],
- 'CEME' => [
- // CEME is NOT a valid 5.5.1 tag
- //'BURI',
- ],
- 'RELA' => [
- 'ASSO',
- '_ASSO',
- ],
- 'DATE' => [
- 'ADOP',
- 'ANUL',
- 'BAPL',
- 'BAPM',
- 'BARM',
- 'BASM',
- 'BIRT',
- 'BLES',
- 'BURI',
- 'CENS',
- 'CENS',
- 'CHR',
- 'CHRA',
- 'CONF',
- 'CONL',
- 'CREM',
- 'DEAT',
- 'DIV',
- 'DIVF',
- 'DSCR',
- 'EDUC',
- 'EMIG',
- 'ENDL',
- 'ENGA',
- 'EVEN',
- 'FCOM',
- 'GRAD',
- 'IMMI',
- 'MARB',
- 'MARC',
- 'MARL',
- 'MARR',
- 'MARS',
- 'NATU',
- 'OCCU',
- 'ORDN',
- 'PROB',
- 'PROP',
- 'RELI',
- 'RESI',
- 'RETI',
- 'SLGC',
- 'SLGS',
- 'TITL',
- 'WILL',
- '_TODO',
- ],
- 'AGE' => [
- 'CENS',
- 'DEAT',
- ],
- 'TEMP' => [
- 'BAPL',
- 'CONL',
- 'ENDL',
- 'SLGC',
- 'SLGS',
- ],
- 'PLAC' => [
- 'ADOP',
- 'ANUL',
- 'BAPL',
- 'BAPM',
- 'BARM',
- 'BASM',
- 'BIRT',
- 'BLES',
- 'BURI',
- 'CENS',
- 'CHR',
- 'CHRA',
- 'CONF',
- 'CONL',
- 'CREM',
- 'DEAT',
- 'DIV',
- 'DIVF',
- 'EDUC',
- 'EMIG',
- 'ENDL',
- 'ENGA',
- 'EVEN',
- 'FCOM',
- 'GRAD',
- 'IMMI',
- 'MARB',
- 'MARC',
- 'MARL',
- 'MARR',
- 'MARS',
- 'NATU',
- 'OCCU',
- 'ORDN',
- 'PROB',
- 'PROP',
- 'RELI',
- 'RESI',
- 'RETI',
- 'SLGC',
- 'SLGS',
- 'SSN',
- 'TITL',
- 'WILL',
- ],
- 'STAT' => [
- 'BAPL',
- 'CONL',
- 'ENDL',
- 'SLGC',
- 'SLGS',
- ],
- 'ADDR' => [
- 'BAPM',
- 'BIRT',
- 'BURI',
- 'CENS',
- 'CHR',
- 'CHRA',
- 'CONF',
- 'CREM',
- 'DEAT',
- 'EDUC',
- 'EVEN',
- 'GRAD',
- 'MARR',
- 'OCCU',
- 'ORDN',
- 'PROP',
- 'RESI',
- ],
- 'CAUS' => [
- 'DEAT',
- ],
- 'PHON' => [
- 'OCCU',
- 'RESI',
- ],
- 'FAX' => [
- 'OCCU',
- 'RESI',
- ],
- 'WWW' => [
- 'OCCU',
- 'RESI',
- ],
- 'EMAIL' => [
- 'OCCU',
- 'RESI',
- ],
- 'HUSB' => [
- 'MARR',
- ],
- 'WIFE' => [
- 'MARR',
- ],
- 'FAMC' => [
- 'ADOP',
- 'SLGC',
- ],
- 'EVEN' => [
- 'DATA',
- ],
- '_WT_USER' => [
- '_TODO',
- ],
- // See https://bugs.launchpad.net/webtrees/+bug/1082666
- 'RELI' => [
- 'CHR',
- 'CHRA',
- 'BAPM',
- 'MARR',
- 'BURI',
- ],
- ];
- }
-
- /**
- * A list of facts/events that generally have two associates
- * (two witnesses, two godparents, etc.)
- *
- * @return array<string>
- */
- public static function twoAssociates(): array
- {
- return [
- 'CHR',
- 'BAPM',
- 'MARR',
- ];
- }
-}
diff --git a/app/Contracts/ElementInterface.php b/app/Contracts/ElementInterface.php
index 145e3a50f4..b47890d8e1 100644
--- a/app/Contracts/ElementInterface.php
+++ b/app/Contracts/ElementInterface.php
@@ -91,7 +91,7 @@ interface ElementInterface
*
* @return void
*/
- public function subtag(string $subtag, string $repeat, string $before): void;
+ public function subtag(string $subtag, string $repeat = '0:1', string $before = ''): void;
/**
* @return array<string,string>
diff --git a/app/Elements/AbstractElement.php b/app/Elements/AbstractElement.php
index 1886fa85bc..f27300605d 100644
--- a/app/Elements/AbstractElement.php
+++ b/app/Elements/AbstractElement.php
@@ -218,7 +218,7 @@ abstract class AbstractElement implements ElementInterface
*
* @return void
*/
- public function subtag(string $subtag, string $repeat, string $before): void
+ public function subtag(string $subtag, string $repeat = '0:1', string $before = ''): void
{
if ($repeat === '') {
unset($this->subtags[$subtag]);
@@ -229,7 +229,7 @@ abstract class AbstractElement implements ElementInterface
foreach ($this->subtags as $key => $value) {
if ($key === $before) {
- $tmp[$key] = $repeat;
+ $tmp[$subtag] = $repeat;
}
$tmp[$key] = $value;
}
diff --git a/app/Functions/FunctionsEdit.php b/app/Functions/FunctionsEdit.php
deleted file mode 100644
index c59d520b79..0000000000
--- a/app/Functions/FunctionsEdit.php
+++ /dev/null
@@ -1,911 +0,0 @@
-<?php
-
-/**
- * webtrees: online genealogy
- * Copyright (C) 2021 webtrees development team
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-declare(strict_types=1);
-
-namespace Fisharebest\Webtrees\Functions;
-
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Census\Census;
-use Fisharebest\Webtrees\Config;
-use Fisharebest\Webtrees\Date;
-use Fisharebest\Webtrees\Elements\PafUid;
-use Fisharebest\Webtrees\Fact;
-use Fisharebest\Webtrees\Family;
-use Fisharebest\Webtrees\Gedcom;
-use Fisharebest\Webtrees\GedcomTag;
-use Fisharebest\Webtrees\Html;
-use Fisharebest\Webtrees\Http\RequestHandlers\AutoCompleteCitation;
-use Fisharebest\Webtrees\Http\RequestHandlers\AutoCompletePlace;
-use Fisharebest\Webtrees\Http\RequestHandlers\AutoCompleteSurname;
-use Fisharebest\Webtrees\Http\RequestHandlers\CreateMediaObjectModal;
-use Fisharebest\Webtrees\Http\RequestHandlers\CreateNoteModal;
-use Fisharebest\Webtrees\Http\RequestHandlers\CreateRepositoryModal;
-use Fisharebest\Webtrees\Http\RequestHandlers\CreateSourceModal;
-use Fisharebest\Webtrees\Http\RequestHandlers\CreateSubmitterModal;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Individual;
-use Fisharebest\Webtrees\Module\CensusAssistantModule;
-use Fisharebest\Webtrees\Registry;
-use Fisharebest\Webtrees\Services\LocalizationService;
-use Fisharebest\Webtrees\Services\MessageService;
-use Fisharebest\Webtrees\Services\ModuleService;
-use Fisharebest\Webtrees\Services\UserService;
-use Fisharebest\Webtrees\Tree;
-use Psr\Http\Message\ServerRequestInterface;
-use Ramsey\Uuid\Uuid;
-
-use function app;
-use function array_merge;
-use function array_slice;
-use function count;
-use function date;
-use function e;
-use function explode;
-use function implode;
-use function in_array;
-use function preg_match;
-use function preg_match_all;
-use function route;
-use function str_contains;
-use function strtolower;
-use function strtoupper;
-use function substr;
-use function trim;
-use function ucfirst;
-use function view;
-
-/**
- * Class FunctionsEdit - common functions for editing
- *
- * @deprecated since 2.0.6. Will be removed in 2.1.0
- */
-class FunctionsEdit
-{
- /** @var string[] - a list of GEDCOM tags in the edit form. */
- private static $tags = [];
-
- /**
- * Function edit_language_checkboxes
- *
- * @param string $parameter_name
- * @param array<string> $languages
- *
- * @return string
- */
- public static function editLanguageCheckboxes(string $parameter_name, array $languages): string
- {
- return view('edit/language-checkboxes', ['languages' => $languages]);
- }
-
- /**
- * A list of access levels (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsAccessLevels(): array
- {
- return Auth::accessLevelNames();
- }
-
- /**
- * A list of active languages (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsActiveLanguages(): array
- {
- $languages = [];
- foreach (I18N::activeLocales() as $locale) {
- $languages[$locale->languageTag()] = $locale->endonym();
- }
-
- return $languages;
- }
-
- /**
- * A list of calendar conversions (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsCalendarConversions(): array
- {
- return ['none' => I18N::translate('No calendar conversion')] + Date::calendarNames();
- }
-
- /**
- * A list of contact methods (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsContactMethods(): array
- {
- return app(MessageService::class)->contactMethods();
- }
-
- /**
- * A list of hide/show options (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsHideShow(): array
- {
- return [
- '0' => I18N::translate('no'),
- '1' => I18N::translate('yes'),
- ];
- }
-
- /**
- * A list of integers (e.g. for an edit control).
- *
- * @param array<int> $integers
- *
- * @return array<int,string>
- */
- public static function numericOptions(array $integers): array
- {
- $array = [];
- foreach ($integers as $integer) {
- if ($integer === -1) {
- $array[$integer] = I18N::translate('All');
- } else {
- $array[$integer] = I18N::number($integer);
- }
- }
-
- return $array;
- }
-
- /**
- * A list of no/yes options (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsNoYes(): array
- {
- return [
- '0' => I18N::translate('no'),
- '1' => I18N::translate('yes'),
- ];
- }
-
- /**
- * A list of GEDCOM restrictions for inline data.
- *
- * @param bool $include_empty
- *
- * @return array<string>
- */
- public static function optionsRestrictions(bool $include_empty): array
- {
- $options = [
- 'none' => I18N::translate('Show to visitors'),
- 'privacy' => I18N::translate('Show to members'),
- 'confidential' => I18N::translate('Show to managers'),
- 'locked' => I18N::translate('Only managers can edit'),
- ];
-
- if ($include_empty) {
- $options = ['' => ''] + $options;
- }
-
- return $options;
- }
-
- /**
- * A list of GEDCOM restrictions for privacy rules.
- *
- * @return array<string>
- */
- public static function optionsRestrictionsRule(): array
- {
- return Auth::privacyRuleNames();
- }
-
- /**
- * A list of temple options (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsTemples(): array
- {
- return Registry::elementFactory()->make('SUBN:TEMP')->values();
- }
-
- /**
- * A list of user options (e.g. for an edit control).
- *
- * @return array<string>
- */
- public static function optionsUsers(): array
- {
- $options = ['' => '-'];
-
- foreach (app(UserService::class)->all() as $user) {
- $options[$user->userName()] = $user->realName() . ' - ' . $user->userName();
- }
-
- return $options;
- }
-
- /**
- * add a new tag input field
- * called for each fact to be edited on a form.
- * Fact level=0 means a new empty form : data are POSTed by name
- * else data are POSTed using arrays :
- * glevels[] : tag level
- * islink[] : tag is a link
- * tag[] : tag name
- * text[] : tag value
- *
- * @param Tree $tree
- * @param string $tag fact record to edit (eg 2 DATE xxxxx)
- * @param string $upperlevel optional upper level tag (eg BIRT)
- * @param string $label An optional label to echo instead of the default
- *
- * @return string
- */
- public static function addSimpleTag(Tree $tree, string $tag, string $upperlevel = '', string $label = ''): string
- {
- $localization_service = app(LocalizationService::class);
-
- $request = app(ServerRequestInterface::class);
- $xref = $request->getAttribute('xref', '');
-
- // Some form fields need access to previous form fields.
- static $previous_ids = [
- 'SOUR' => '',
- 'PLAC' => '',
- ];
-
- $parts = explode(' ', $tag, 3);
- $level = $parts[0] ?? '';
- $fact = $parts[1] ?? '';
- $value = $parts[2] ?? '';
-
- if ($level === '0') {
- // Adding a new fact.
- if ($upperlevel) {
- $name = $upperlevel . '_' . $fact;
- } else {
- $name = $fact;
- }
- } else {
- // Editing an existing fact.
- $name = 'text[]';
- }
-
- $id = $fact . Uuid::uuid4()->toString();
-
- $previous_ids[$fact] = $id;
-
- // field value
- $islink = (bool) preg_match('/^@[^#@][^@]*@$/', $value);
- if ($islink) {
- $value = trim($value, '@');
- }
-
- if ($fact === 'REPO' || $fact === 'SOUR' || $fact === 'OBJE' || $fact === 'FAMC' || $fact === 'SUBM' || $fact === 'ASSO' || $fact === '_ASSO' || $fact === 'ALIA') {
- $islink = true;
- }
-
- if ($fact === 'SHARED_NOTE_EDIT' || $fact === 'SHARED_NOTE') {
- $islink = true;
- $fact = 'NOTE';
- }
-
- $row_class = 'form-group row';
- switch ($fact) {
- case 'DATA':
- case 'MAP':
- // These GEDCOM tags should have no data, just child tags.
- if ($value === '') {
- $row_class .= ' d-none';
- }
- break;
- case 'LATI':
- case 'LONG':
- // Indicate that this row is a child of a previous row, so we can expand/collapse them.
- $row_class .= ' child_of_' . $previous_ids['PLAC'];
- if ($value === '') {
- $row_class .= ' collapse';
- }
- break;
- }
-
- $html = '';
- $html .= '<div class="' . $row_class . '">';
- $html .= '<label class="col-sm-3 col-form-label" for="' . $id . '">';
-
- // tag name
- if ($label) {
- $html .= $label;
- } elseif ($upperlevel) {
- $html .= GedcomTag::getLabel($upperlevel . ':' . $fact);
- } else {
- $html .= GedcomTag::getLabel($fact);
- }
-
- // Not all facts have help text.
- switch ($fact) {
- case 'NAME':
- if ($upperlevel !== 'REPO' && $upperlevel !== 'UNKNOWN') {
- $html .= view('help/link', ['topic' => $fact]);
- }
- break;
- case 'ROMN':
- case 'SURN':
- case '_HEB':
- $html .= view('help/link', ['topic' => $fact]);
- break;
- }
-
- // tag level
- if ($level !== '0') {
- $html .= '<input type="hidden" name="glevels[]" value="' . $level . '">';
- $html .= '<input type="hidden" name="islink[]" value="' . $islink . '">';
- $html .= '<input type="hidden" name="tag[]" value="' . $fact . '">';
- }
- $html .= '</label>';
-
- // value
- $html .= '<div class="col-sm-9">';
-
- // Show names for spouses in MARR/HUSB/AGE and MARR/WIFE/AGE
- if ($fact === 'HUSB' || $fact === 'WIFE') {
- $family = Registry::familyFactory()->make($xref, $tree);
- if ($family instanceof Family) {
- $spouse_link = $family->facts([$fact])->first();
- if ($spouse_link instanceof Fact) {
- $spouse = $spouse_link->target();
- if ($spouse instanceof Individual) {
- $html .= $spouse->fullName();
- }
- }
- }
- }
-
- if (in_array($fact, Config::emptyFacts(), true) && ($value === '' || $value === 'Y' || $value === 'y')) {
- $html .= '<input type="hidden" id="' . $id . '" name="' . $name . '" value="' . $value . '">';
-
- $checked = $value === '' ? '' : 'checked';
- $onchange = 'this.previousSibling.value=this.checked ? this.value : &quot;&quot;';
- $html .= '<input type="checkbox" value="Y" ' . $checked . ' onchange="' . $onchange . '">';
-
- if ($fact === 'CENS' && $value === 'Y') {
- $html .= view('modules/GEDFact_assistant/select-census', [
- 'census_places' => Census::censusPlaces(I18N::languageTag()),
- ]);
-
- $census_assistant = app(ModuleService::class)->findByInterface(CensusAssistantModule::class)->first();
- $record = Registry::individualFactory()->make($xref, $tree);
-
- if ($census_assistant instanceof CensusAssistantModule && $record instanceof Individual) {
- $html .= $census_assistant->createCensusAssistant($record);
- }
- }
- } elseif ($fact === 'NPFX' || $fact === 'NSFX' || $fact === 'SPFX' || $fact === 'NICK') {
- $html .= '<div class="input-group">';
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="updatewholename()">';
- $html .= view('edit/input-addon-keyboard', ['id' => $id]);
- $html .= '</div>';
- } elseif ($fact === 'GIVN') {
- $html .= '<div class="input-group">';
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="updatewholename()" autofocus>';
- $html .= view('edit/input-addon-keyboard', ['id' => $id]);
- $html .= '</div>';
- } elseif ($fact === 'SURN' || $fact === '_MARNM_SURN') {
- $html .= '<div class="input-group">';
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" autocomplete="off" data-autocomplete-url="' . e(route(AutoCompleteSurname::class, ['tree' => $tree->name()])) . '" oninput="updatewholename()" onblur="updatewholename()">';
- $html .= view('edit/input-addon-keyboard', ['id' => $id]);
- $html .= '</div>';
- } elseif ($fact === 'ADOP') {
- $element = Registry::elementFactory()->make('INDI:ADOP:FAMC:ADOP');
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => $element->values()]);
- } elseif ($fact === 'LANG') {
- $element = Registry::elementFactory()->make('HEAD:LANG');
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => $element->values()]);
- } elseif ($fact === 'ALIA') {
- $html .= '<div class="input-group">';
- $html .= view('components/select-individual', ['id' => $id, 'name' => $name, 'individual' => Registry::individualFactory()->make($value, $tree), 'tree' => $tree]);
- $html .= '</div>';
- } elseif ($fact === 'ASSO' || $fact === '_ASSO') {
- $html .= '<div class="input-group">';
- $html .= view('components/select-individual', ['id' => $id, 'name' => $name, 'individual' => Registry::individualFactory()->make($value, $tree), 'tree' => $tree]);
- $html .= '</div>';
- if ($level === '1') {
- $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>';
- } else {
- $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>';
- }
- } elseif ($fact === 'DATE') {
- // Need to know if the user prefers DMY/MDY/YMD so we can validate dates properly.
- $dmy = '"' . $localization_service->dateFormatToOrder(I18N::dateFormat()) . '"';
-
- $html .= '<div class="input-group">';
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" onchange="webtrees.reformatDate(this, ' . e($dmy) . ')" dir="ltr">';
- $html .= view('edit/input-addon-calendar', ['id' => $id]);
- $html .= view('edit/input-addon-help', ['fact' => 'DATE']);
- $html .= '</div>';
- $html .= '<div id="caldiv' . $id . '" style="position:absolute;visibility:hidden;background-color:white;z-index:1000"></div>';
- $html .= '<p class="text-muted">' . (new Date($value))->display() . '</p>';
- } elseif ($fact === 'FAMC') {
- $html .= view('components/select-family', ['id' => $id, 'name' => $name, 'family' => Registry::familyFactory()->make($value, $tree), 'tree' => $tree]);
- } elseif ($fact === 'LATI') {
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="webtrees.reformatLatitude(this)">';
- } elseif ($fact === 'LONG') {
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '" oninput="webtrees.reformatLongitude(this)">';
- } elseif ($fact === 'NOTE' && $islink) {
- $html .=
- '<div class="input-group">' .
- '<div class="input-group-prepend">' .
- '<button class="btn btn-secondary" type="button" data-toggle="modal" data-backdrop="static" data-target="#wt-ajax-modal" data-href="' . e(route(CreateNoteModal::class, ['tree' => $tree->name()])) . '" data-select-id="' . $id . '" title="' . I18N::translate('Create a shared note') . '">' .
- '' . view('icons/add') . '<' .
- '/button>' .
- '</div>' .
- view('components/select-note', ['id' => $id, 'name' => $name, 'note' => Registry::noteFactory()->make($value, $tree), 'tree' => $tree]) .
- '</div>';
- } elseif ($fact === 'OBJE') {
- $html .=
- '<div class="input-group">' .
- '<div class="input-group-prepend"><button class="btn btn-secondary" type="button" data-toggle="modal" data-backdrop="static" data-href="' . e(route(CreateMediaObjectModal::class, ['tree' => $tree->name()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a media object') . '">' . view('icons/add') . '</button></div>' .
- view('components/select-media', ['id' => $id, 'name' => $name, 'media' => Registry::mediaFactory()->make($value, $tree), 'tree' => $tree]) .
- '</div>';
- } elseif ($fact === 'PAGE') {
- $html .= '<input ' . Html::attributes([
- 'autocomplete' => 'off',
- 'class' => 'form-control',
- 'id' => $id,
- 'name' => $name,
- 'value' => $value,
- 'type' => 'text',
- 'data-autocomplete-url' => route(AutoCompleteCitation::class, ['tree' => $tree->name()]),
- 'data-autocomplete-extra' => 'SOUR',
- ]) . '>';
- } elseif ($fact === 'PEDI') {
- $element = Registry::elementFactory()->make('INDI:FAMC:PEDI');
-
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => $element->values()]);
- } elseif ($fact === 'PLAC') {
- $html .= '<div class="input-group">';
- $html .= '<input ' . Html::attributes([
- 'autocomplete' => 'off',
- 'class' => 'form-control',
- 'id' => $id,
- 'name' => $name,
- 'value' => $value,
- 'type' => 'text',
- 'data-autocomplete-url' => route(AutoCompletePlace::class, ['tree' => $tree->name()]),
- ]) . '>';
-
- $html .= view('edit/input-addon-coordinates', ['id' => $id]);
- $html .= view('edit/input-addon-help', ['fact' => 'PLAC']);
- $html .= '</div>';
- } elseif ($fact === 'QUAY') {
- $element = Registry::elementFactory()->make('INDI:SOUR:QUAY');
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => $element->values()]);
- } elseif ($fact === 'RELA') {
- $html .= Registry::elementFactory()->make('INDI:ASSO:RELA')->edit($id, $name, $value, $tree);
- } elseif ($fact === 'REPO') {
- $html .=
- '<div class="input-group">' .
- '<div class="input-group-prepend"><button class="btn btn-secondary" type="button" data-toggle="modal" data-backdrop="static" data-href="' . e(route(CreateRepositoryModal::class, ['tree' => $tree->name()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a repository') . '">' . view('icons/add') . '</button></div>' .
- view('components/select-repository', ['id' => $id, 'name' => $name, 'repository' => Registry::repositoryFactory()->make($value, $tree), 'tree' => $tree]) .
- '</div>';
- } elseif ($fact === 'RESN') {
- $html .= '<div class="input-group">';
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => self::optionsRestrictions(true)]);
- $html .= view('edit/input-addon-help', ['fact' => 'RESN']);
- $html .= '</span>';
- $html .= '</div>';
- } elseif ($fact === 'SEX') {
- $html .= view('components/radios-inline', ['name' => $name, 'options' => ['M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown')], 'selected' => $value]);
- } elseif ($fact === 'SOUR') {
- $html .=
- '<div class="input-group">' .
- '<div class="input-group-prepend"><button class="btn btn-secondary" type="button" data-toggle="modal" data-backdrop="static" data-href="' . e(route(CreateSourceModal::class, ['tree' => $tree->name()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a source') . '">' . view('icons/add') . '</button></div>' .
- view('components/select-source', ['id' => $id, 'name' => $name, 'source' => Registry::sourceFactory()->make($value, $tree), 'tree' => $tree]) .
- '</div>';
- } elseif ($fact === 'STAT') {
- $html .= Registry::elementFactory()->make(($upperlevel === 'SLGS' ? 'FAM:' : 'INDI:') . $upperlevel . ':STAT')->edit($id, $name, $value, $tree);
- } elseif ($fact === 'SUBM') {
- $html .=
- '<div class="input-group">' .
- '<div class="input-group-prepend"><button class="btn btn-secondary" type="button" data-toggle="modal" data-backdrop="static" data-href="' . e(route(CreateSubmitterModal::class, ['tree' => $tree->name()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a submitter') . '">' . view('icons/add') . '</button></div>' .
- view('components/select-submitter', ['id' => $id, 'name' => $name, 'submitter' => Registry::submitterFactory()->make($value, $tree), 'tree' => $tree]) .
- '</div>';
- } elseif ($fact === 'TEMP') {
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => self::optionsTemples()]);
- } elseif ($fact === 'TIME') {
- /* I18N: Examples of valid time formats (hours:minutes:seconds) */
- $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::translate('hh:mm or hh:mm:ss') . '">';
- } elseif ($fact === '_WT_USER') {
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => self::optionsUsers()]);
- } elseif ($fact === '_PRIM') {
- $html .= view('components/select', ['id' => $id, 'name' => $name, 'selected' => $value, 'options' => ['' => '', 'Y' => I18N::translate('always'), 'N' => I18N::translate('never')]]);
- $html .= '<p class="small text-muted">' . I18N::translate('Use this image for charts and on the individual’s page.') . '</p>';
- } elseif ($fact === 'TYPE' && $level === '0') {
- // Level 0 TYPE fields are only used for NAME records
- $html .= Registry::elementFactory()->make('INDI:NAME:TYPE')->edit($id, $name, $value, $tree);
- } elseif (($fact !== 'NAME' || $upperlevel === 'REPO' || $upperlevel === 'SUBM' || $upperlevel === 'UNKNOWN') && $fact !== '_MARNM') {
- if ($fact === 'TEXT' || $fact === 'ADDR' || ($fact === 'NOTE' && !$islink)) {
- $html .= '<div class="input-group">';
- $html .= '<textarea class="form-control" id="' . $id . '" name="' . $name . '" rows="5" dir="auto">' . e($value) . '</textarea>';
- $html .= '</div>';
- } else {
- // If using GEDFact-assistant window
- $html .= '<input class="form-control" type="text" id="' . $id . '" name="' . $name . '" value="' . e($value) . '">';
- }
- } else {
- // Populated in javascript from sub-tags
- $html .= '<input type="hidden" id="' . $id . '" name="' . $name . '" oninput="updateTextName(\'' . $id . '\')" value="' . e($value) . '" class="' . $fact . '">';
- $html .= '<span id="' . $id . '_display" dir="auto">' . e($value) . '</span>';
- $html .= ' <a href="#edit_name" onclick="convertHidden(\'' . $id . '\'); return false" class="icon-edit_indi" title="' . I18N::translate('Edit the name') . '"></a>';
- }
- // MARRiage TYPE : hide text field and show a selection list
- if ($fact === 'TYPE' && $level === '2' && self::$tags[0] === 'MARR') {
- $html .= '<script>';
- $html .= 'document.getElementById(\'' . $id . '\').style.display=\'none\'';
- $html .= '</script>';
- $html .= '<select id="' . $id . '_sel" oninput="document.getElementById(\'' . $id . '\').value=this.value" >';
-
- $marriage_types = [
- '' => '',
- 'Civil' => I18N::translate('Civil marriage'),
- 'Religious' => I18N::translate('Religious marriage'),
- 'Partners' => I18N::translate('Registered partnership'),
- ];
-
- foreach ($marriage_types as $key => $type_label) {
- $html .= '<option value="' . $key . '" ';
- if (strtolower($key) === strtolower($value)) {
- $html .= 'selected';
- }
- $html .= '>' . $type_label . '</option>';
- }
- $html .= '</select>';
- }
-
- $html .= '</div></div>';
-
- return $html;
- }
-
- /**
- * Add some empty tags to create a new fact.
- *
- * @param Tree $tree
- * @param string $fact
- *
- * @return void
- */
- public static function addSimpleTags(Tree $tree, string $fact): void
- {
- // For new individuals, these facts default to "Y"
- if ($fact === 'MARR') {
- echo self::addSimpleTag($tree, '0 ' . $fact . ' Y');
- } else {
- echo self::addSimpleTag($tree, '0 ' . $fact);
- }
-
- if (!in_array($fact, Config::nonDateFacts(), true)) {
- echo self::addSimpleTag($tree, '0 DATE', $fact, GedcomTag::getLabel($fact . ':DATE'));
- }
-
- if (!in_array($fact, Config::nonPlaceFacts(), true)) {
- echo self::addSimpleTag($tree, '0 PLAC', $fact, GedcomTag::getLabel($fact . ':PLAC'));
-
- if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
- foreach ($match[1] as $tag) {
- echo self::addSimpleTag($tree, '0 ' . $tag, $fact, GedcomTag::getLabel($fact . ':PLAC:' . $tag));
- }
- }
- echo self::addSimpleTag($tree, '0 MAP', $fact);
- echo self::addSimpleTag($tree, '0 LATI', $fact);
- echo self::addSimpleTag($tree, '0 LONG', $fact);
- }
- }
-
- /**
- * builds the form for adding new facts
- *
- * @param Tree $tree
- * @param string $fact the new fact we are adding
- *
- * @return void
- */
- public static function createAddForm(Tree $tree, string $fact): void
- {
- self::$tags = [];
-
- // handle MARRiage TYPE
- if (substr($fact, 0, 5) === 'MARR_') {
- self::$tags[0] = 'MARR';
- echo self::addSimpleTag($tree, '1 MARR');
- self::insertMissingSubtags($tree, $fact);
- } else {
- self::$tags[0] = $fact;
- if ($fact === '_UID') {
- $fact .= ' ' . (new PafUid(''))->default($tree);
- }
- // These new level 1 tags need to be turned into links
- if (in_array($fact, ['ALIA', 'ASSO'], true)) {
- $fact .= ' @';
- }
- if (in_array($fact, Config::emptyFacts(), true)) {
- echo self::addSimpleTag($tree, '1 ' . $fact . ' Y');
- } else {
- echo self::addSimpleTag($tree, '1 ' . $fact);
- }
- self::insertMissingSubtags($tree, self::$tags[0]);
- //-- handle the special SOURce case for level 1 sources [ 1759246 ]
- if ($fact === 'SOUR') {
- echo self::addSimpleTag($tree, '2 PAGE');
- echo self::addSimpleTag($tree, '2 DATA');
- echo self::addSimpleTag($tree, '3 TEXT');
- if ($tree->getPreference('FULL_SOURCES')) {
- echo self::addSimpleTag($tree, '3 DATE', '', GedcomTag::getLabel('DATA:DATE'));
- echo self::addSimpleTag($tree, '2 QUAY');
- }
- }
- }
- }
-
- /**
- * Create a form to edit a Fact object.
- *
- * @param Fact $fact
- *
- * @return void
- */
- public static function createEditForm(Fact $fact): void
- {
- $record = $fact->record();
- $tree = $record->tree();
-
- self::$tags = [];
-
- $level0type = $record->tag();
- $level1type = $fact->getTag();
-
- // List of tags we would expect at the next level
- // NB insertMissingSubtags() already takes care of the simple cases
- // where a level 1 tag is missing a level 2 tag. Here we only need to
- // handle the more complicated cases.
- $expected_subtags = [
- 'SOUR' => [
- 'PAGE',
- 'DATA',
- ],
- 'PLAC' => ['MAP'],
- 'MAP' => [
- 'LATI',
- 'LONG',
- ],
- ];
-
- if ($record->tag() !== 'SOUR') {
- //source citations within other records, i.e. n SOUR / +1 DATA / +2 TEXT
- $expected_subtags['DATA'][] = 'TEXT';
- } //else: source records themselves, i.e. 0 SOUR / 1 DATA don't get a 2 TEXT!
-
- if ($record->tag() === 'SOUR') {
- //source records themselves, i.e. 0 SOUR / 1 DATA / 2 EVEN get a 3 DATE and a 3 PLAC
- $expected_subtags['EVEN'][] = 'DATE';
- $expected_subtags['EVEN'][] = 'PLAC';
- }
-
- if ($record->tree()->getPreference('FULL_SOURCES')) {
- $expected_subtags['SOUR'][] = 'QUAY';
-
- if ($record->tag() !== 'SOUR') {
- //source citations within other records, i.e. n SOUR / +1 DATA / +2 DATE
- $expected_subtags['DATA'][] = 'DATE';
- } //else: source records themselves, i.e. 0 SOUR / 1 DATA don't get a 2 DATE!
- }
-
- if ($level1type === 'BAPL' || $level1type === 'CONL' || $level1type === 'ENDL' || $level1type === 'SLGC' || $level1type === 'SLGS') {
- $expected_subtags['STAT'] = ['DATE'];
- }
-
- if (in_array($level1type, Config::dateAndTime(), true)) {
- // TIME is NOT a valid 5.5.1 tag
- $expected_subtags['DATE'] = ['TIME'];
- }
-
- if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $record->tree()->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
- $expected_subtags['PLAC'] = array_merge($match[1], $expected_subtags['PLAC']);
- }
-
- $stack = [];
- $gedlines = explode("\n", $fact->gedcom());
- $count = count($gedlines);
- $i = 0;
- $inSource = false;
- $levelSource = 0;
- $add_date = true;
-
- // Loop on existing tags :
- while ($i < $count) {
- $fields = explode(' ', $gedlines[$i], 3);
- $level = (int) $fields[0];
- $type = $fields[1] ?? '';
- $text = $fields[2] ?? '';
-
- // Keep track of our hierarchy, e.g. 1=>BIRT, 2=>PLAC, 3=>FONE
- $stack[$level] = $type;
- // Merge them together, e.g. BIRT:PLAC:FONE
- $label = implode(':', array_slice($stack, 0, $level));
-
- // Merge text from continuation lines
- while ($i + 1 < $count && preg_match('/^' . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0) {
- $text .= "\n" . $cmatch[1];
- $i++;
- }
-
- if ($type === 'SOUR') {
- $inSource = true;
- $levelSource = $level;
- } elseif ($levelSource >= $level) {
- $inSource = false;
- }
-
- self::$tags[] = $type;
- $subrecord = $level . ' ' . $type . ' ' . $text;
-
- // Dates need different labels, depending on whether they are inside sources.
- if ($inSource && $type === 'DATE') {
- echo self::addSimpleTag($tree, $subrecord, '', GedcomTag::getLabel($label));
- } elseif (!$inSource && $type === 'DATE') {
- echo self::addSimpleTag($tree, $subrecord, $level1type, GedcomTag::getLabel($label));
- if ($level === 2) {
- // We already have a date - no need to add one.
- $add_date = false;
- }
- } elseif ($type === 'STAT') {
- echo self::addSimpleTag($tree, $subrecord, $level1type, GedcomTag::getLabel($label));
- } else {
- echo self::addSimpleTag($tree, $subrecord, $level0type, GedcomTag::getLabel($label));
- }
-
- // Get a list of tags present at the next level
- $subtags = [];
- for ($ii = $i + 1; isset($gedlines[$ii]) && preg_match('/^(\d+) (\S+)/', $gedlines[$ii], $mm) && $mm[1] > $level; ++$ii) {
- if ($mm[1] == $level + 1) {
- $subtags[] = $mm[2];
- }
- }
-
- // Insert missing tags
- foreach ($expected_subtags[$type] ?? [] as $subtag) {
- if (!in_array($subtag, $subtags, true)) {
- echo self::addSimpleTag($tree, ($level + 1) . ' ' . $subtag, '', GedcomTag::getLabel($label . ':' . $subtag));
- foreach ($expected_subtags[$subtag] ?? [] as $subsubtag) {
- echo self::addSimpleTag($tree, ($level + 2) . ' ' . $subsubtag, '', GedcomTag::getLabel($label . ':' . $subtag . ':' . $subsubtag));
- }
- }
- }
-
- $i++;
- }
-
- if ($level1type !== '_PRIM') {
- //0 SOUR / 1 DATA doesn't get a 2 DATE!
- //0 SOUR / 1 DATA doesn't get a 2 EVEN here either, we rather handle this via cards/add-sour-data-even
- if ($record->tag() !== 'SOUR') {
- self::insertMissingSubtags($tree, $level1type, $add_date);
- }
- }
- }
-
- /**
- * Populates the global $tags array with any missing sub-tags.
- *
- * @param Tree $tree
- * @param string $level1tag the type of the level 1 gedcom record
- * @param bool $add_date
- *
- * @return void
- */
- public static function insertMissingSubtags(Tree $tree, string $level1tag, bool $add_date = false): void
- {
- // handle MARRiage TYPE
- $type_val = '';
- if (substr($level1tag, 0, 5) === 'MARR_') {
- $type_val = ucfirst(strtolower(substr($level1tag, 5)));
- $level1tag = 'MARR';
- }
-
- foreach (Config::levelTwoTags() as $key => $value) {
- if ($key === 'DATE' && in_array($level1tag, Config::nonDateFacts(), true) || $key === 'PLAC' && in_array($level1tag, Config::nonPlaceFacts(), true)) {
- continue;
- }
- if (in_array($level1tag, $value, true) && !in_array($key, self::$tags, true)) {
- if ($key === 'TYPE') {
- echo self::addSimpleTag($tree, '2 TYPE ' . $type_val, $level1tag);
- } elseif ($level1tag === '_TODO' && $key === 'DATE') {
- $today = strtoupper(date('d M Y'));
- echo self::addSimpleTag($tree, '2 ' . $key . ' ' . $today, $level1tag);
- } elseif ($level1tag === '_TODO' && $key === '_WT_USER') {
- echo self::addSimpleTag($tree, '2 ' . $key . ' ' . Auth::user()->userName(), $level1tag);
- } elseif ($level1tag === 'NAME' && str_contains($tree->getPreference('ADVANCED_NAME_FACTS'), $key)) {
- echo self::addSimpleTag($tree, '2 ' . $key, $level1tag);
- } elseif ($level1tag !== 'NAME') {
- echo self::addSimpleTag($tree, '2 ' . $key, $level1tag);
- }
- // Add level 3/4 tags as appropriate
- switch ($key) {
- case 'PLAC':
- if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
- foreach ($match[1] as $tag) {
- echo self::addSimpleTag($tree, '3 ' . $tag, '', GedcomTag::getLabel($level1tag . ':PLAC:' . $tag));
- }
- }
- echo self::addSimpleTag($tree, '3 MAP');
- echo self::addSimpleTag($tree, '4 LATI');
- echo self::addSimpleTag($tree, '4 LONG');
- break;
- case 'EVEN':
- echo self::addSimpleTag($tree, '3 DATE');
- echo self::addSimpleTag($tree, '3 PLAC');
- break;
- case 'STAT':
- if ($level1tag === 'BAPL' || $level1tag === 'CONL' || $level1tag === 'ENDL' || $level1tag === 'SLGC' || $level1tag === 'SLGS') {
- echo self::addSimpleTag($tree, '3 DATE', '', GedcomTag::getLabel('STAT:DATE'));
- }
- break;
- case 'DATE':
- // TIME is NOT a valid 5.5.1 tag
- if (in_array($level1tag, Config::dateAndTime(), true)) {
- echo self::addSimpleTag($tree, '3 TIME');
- }
- break;
- case 'HUSB':
- case 'WIFE':
- echo self::addSimpleTag($tree, '3 AGE');
- break;
- case 'FAMC':
- if ($level1tag === 'ADOP') {
- echo self::addSimpleTag($tree, '3 ADOP BOTH');
- }
- break;
- }
- } elseif ($key === 'DATE' && $add_date) {
- echo self::addSimpleTag($tree, '2 DATE', $level1tag, GedcomTag::getLabel($level1tag . ':DATE'));
- }
- }
- // Do something (anything!) with unrecognized custom tags
- if (substr($level1tag, 0, 1) === '_' && $level1tag !== '_UID' && $level1tag !== '_PRIM' && $level1tag !== '_TODO') {
- foreach (['DATE', 'PLAC', 'ADDR', 'AGNC', 'TYPE', 'AGE'] as $tag) {
- if (!in_array($tag, self::$tags, true)) {
- echo self::addSimpleTag($tree, '2 ' . $tag);
- if ($tag === 'PLAC') {
- if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_PLAC_FACTS'), $match)) {
- foreach ($match[1] as $ptag) {
- echo self::addSimpleTag($tree, '3 ' . $ptag, '', GedcomTag::getLabel($level1tag . ':PLAC:' . $ptag));
- }
- }
- echo self::addSimpleTag($tree, '3 MAP');
- echo self::addSimpleTag($tree, '4 LATI');
- echo self::addSimpleTag($tree, '4 LONG');
- }
- }
- }
- }
- }
-}
diff --git a/app/GedcomRecord.php b/app/GedcomRecord.php
index 2dcbd8ef99..737b30a297 100644
--- a/app/GedcomRecord.php
+++ b/app/GedcomRecord.php
@@ -41,6 +41,7 @@ use function e;
use function explode;
use function implode;
use function in_array;
+use function max;
use function md5;
use function preg_match;
use function preg_match_all;
@@ -1352,7 +1353,11 @@ class GedcomRecord
if ($default !== '') {
$gedcom .= ' ' . $default;
}
- $return .= "\n" . $this->insertMissingLevels($tag . ':' . $subtag, $gedcom);
+
+ $number_to_add = max(1, $min - $count);
+ $gedcom_to_add = "\n" . $this->insertMissingLevels($tag . ':' . $subtag, $gedcom);
+
+ $return .= \str_repeat($gedcom_to_add, $number_to_add);
}
}
diff --git a/app/GedcomTag.php b/app/GedcomTag.php
index 8a5a11f697..882c3aff14 100644
--- a/app/GedcomTag.php
+++ b/app/GedcomTag.php
@@ -33,853 +33,9 @@ class GedcomTag
*
* @return string
*/
- public static function getLabel(string $tag): string
+ public static function getLabel($tag): string
{
- switch ($tag) {
- case 'ABBR':
- /* I18N: gedcom tag ABBR */
- return I18N::translate('Abbreviation');
- case 'ADDR':
- /* I18N: gedcom tag ADDR */
- return I18N::translate('Address');
- case 'ADR1':
- /* I18N: gedcom tag ADR1 */
- return I18N::translate('Address line 1');
- case 'ADR2':
- /* I18N: gedcom tag ADR2 */
- return I18N::translate('Address line 2');
- case 'ADR3':
- /* I18N: gedcom tag ADR3 */
- return I18N::translate('Address line 3');
- case 'ADOP':
- /* I18N: gedcom tag ADOP */
- return I18N::translate('Adoption');
- case 'ADOP:DATE':
- return I18N::translate('Date of adoption');
- case 'ADOP:PLAC':
- return I18N::translate('Place of adoption');
- case 'AFN':
- /* I18N: gedcom tag AFN */
- return I18N::translate('Ancestral file number');
- case 'AGE':
- /* I18N: gedcom tag AGE */
- return I18N::translate('Age');
- case 'AGNC':
- /* I18N: gedcom tag AGNC */
- return I18N::translate('Agency');
- case 'ALIA':
- /* I18N: gedcom tag ALIA */
- return I18N::translate('Alias');
- case 'ANCE':
- /* I18N: gedcom tag ANCE */
- return I18N::translate('Generations of ancestors');
- case 'ANCI':
- /* I18N: gedcom tag ANCI */
- return I18N::translate('Ancestors interest');
- case 'ANUL':
- /* I18N: gedcom tag ANUL */
- return I18N::translate('Annulment');
- case 'ASSO':
- /* I18N: gedcom tag ASSO */
- return I18N::translate('Associate');
- case 'AUTH':
- /* I18N: gedcom tag AUTH */
- return I18N::translate('Author');
- case 'BAPL':
- /* I18N: gedcom tag BAPL. LDS = Church of Latter Day Saints. */
- return I18N::translate('LDS baptism');
- case 'BAPL:DATE':
- /* I18N: LDS = Church of Latter Day Saints. */
- return I18N::translate('Date of LDS baptism');
- case 'BAPL:PLAC':
- /* I18N: LDS = Church of Latter Day Saints. */
- return I18N::translate('Place of LDS baptism');
- case 'BAPM':
- /* I18N: gedcom tag BAPM */
- return I18N::translate('Baptism');
- case 'BAPM:DATE':
- return I18N::translate('Date of baptism');
- case 'BAPM:PLAC':
- return I18N::translate('Place of baptism');
- case 'BARM':
- /* I18N: gedcom tag BARM */
- return I18N::translate('Bar mitzvah');
- case 'BARM:DATE':
- return I18N::translate('Date of bar mitzvah');
- case 'BARM:PLAC':
- return I18N::translate('Place of bar mitzvah');
- case 'BASM':
- /* I18N: gedcom tag BASM */
- return I18N::translate('Bat mitzvah');
- case 'BASM:DATE':
- return I18N::translate('Date of bat mitzvah');
- case 'BASM:PLAC':
- return I18N::translate('Place of bat mitzvah');
- case 'BIRT':
- /* I18N: gedcom tag BIRT */
- return I18N::translate('Birth');
- case 'BIRT:DATE':
- return I18N::translate('Date of birth');
- case 'BIRT:PLAC':
- return I18N::translate('Place of birth');
- case 'BLES':
- /* I18N: gedcom tag BLES */
- return I18N::translate('Blessing');
- case 'BLES:DATE':
- return I18N::translate('Date of blessing');
- case 'BLES:PLAC':
- return I18N::translate('Place of blessing');
- case 'BLOB':
- /* I18N: gedcom tag BLOB */
- return I18N::translate('Binary data object');
- case 'BURI':
- /* I18N: gedcom tag BURI */
- return I18N::translate('Burial');
- case 'BURI:DATE':
- return I18N::translate('Date of burial');
- case 'BURI:PLAC':
- return I18N::translate('Place of burial');
- case 'CALN':
- /* I18N: gedcom tag CALN */
- return I18N::translate('Call number');
- case 'CAST':
- /* I18N: gedcom tag CAST */
- return I18N::translate('Caste');
- case 'CAUS':
- /* I18N: gedcom tag CAUS */
- return I18N::translate('Cause');
- case 'CEME':
- /* I18N: gedcom tag CEME */
- return I18N::translate('Cemetery');
- case 'CENS':
- /* I18N: gedcom tag CENS */
- return I18N::translate('Census');
- case 'CENS:DATE':
- return I18N::translate('Census date');
- case 'CENS:PLAC':
- return I18N::translate('Census place');
- case '_UPD': // Family Tree Builder uses "1 _UPD 14 APR 2012 00:14:10 GMT-5" instead of 1 CHAN/2 DATE/3 TIME
- case 'CHAN':
- /* I18N: gedcom tag CHAN */
- return I18N::translate('Last change');
- case 'CHAN:DATE':
- /* I18N: gedcom tag CHAN:DATE */
- return I18N::translate('Date of last change');
- case 'CHAN:_WT_USER':
- /* I18N: gedcom tag CHAN:_WT_USER */
- return I18N::translate('Author of last change');
- case 'CHAR':
- /* I18N: gedcom tag CHAR */
- return I18N::translate('Character set');
- case 'CHIL':
- /* I18N: gedcom tag CHIL */
- return I18N::translate('Child');
- case 'CHR':
- /* I18N: gedcom tag CHR */
- return I18N::translate('Christening');
- case 'CHR:DATE':
- return I18N::translate('Date of christening');
- case 'CHR:PLAC':
- return I18N::translate('Place of christening');
- case 'CHRA':
- /* I18N: gedcom tag CHRA */
- return I18N::translate('Adult christening');
- case 'CITN':
- /* I18N: gedcom tag CITN */
- return I18N::translate('Citizenship');
- case 'CITY':
- /* I18N: gedcom tag CITY */
- return I18N::translate('City');
- case 'COMM':
- /* I18N: gedcom tag COMM */
- return I18N::translate('Comment');
- case 'CONC':
- /* I18N: gedcom tag CONC */
- return I18N::translate('Concatenation');
- case 'CONT':
- /* I18N: gedcom tag CONT */
- return I18N::translate('Continued');
- case 'CONF':
- /* I18N: gedcom tag CONF */
- return I18N::translate('Confirmation');
- case 'CONF:DATE':
- return I18N::translate('Date of confirmation');
- case 'CONF:PLAC':
- return I18N::translate('Place of confirmation');
- case 'CONL':
- /* I18N: gedcom tag CONL. LDS = Church of Latter Day Saints. */
- return I18N::translate('LDS confirmation');
- case 'COPR':
- /* I18N: gedcom tag COPR */
- return I18N::translate('Copyright');
- case 'CORP':
- /* I18N: gedcom tag CORP */
- return I18N::translate('Corporation');
- case 'CREM':
- /* I18N: gedcom tag CREM */
- return I18N::translate('Cremation');
- case 'CREM:DATE':
- return I18N::translate('Date of cremation');
- case 'CREM:PLAC':
- return I18N::translate('Place of cremation');
- case 'CTRY':
- /* I18N: gedcom tag CTRY */
- return I18N::translate('Country');
- case 'DATA':
- /* I18N: gedcom tag DATA */
- return I18N::translate('Data');
- case 'DATA:DATE':
- return I18N::translate('Date of entry in original source');
- case '_DATE': // Family Tree Builder uses OBJE:_DATE
- case 'DATE':
- /* I18N: gedcom tag DATE */
- return I18N::translate('Date');
- case 'DEAT':
- /* I18N: gedcom tag DEAT */
- return I18N::translate('Death');
- case 'DEAT:CAUS':
- return I18N::translate('Cause of death');
- case 'DEAT:DATE':
- return I18N::translate('Date of death');
- case 'DEAT:PLAC':
- return I18N::translate('Place of death');
- case 'DESC':
- /* I18N: gedcom tag DESC */
- return I18N::translate('Descendants');
- case 'DESI':
- /* I18N: gedcom tag DESI */
- return I18N::translate('Descendants interest');
- case 'DEST':
- /* I18N: gedcom tag DEST */
- return I18N::translate('Destination');
- case 'DIV':
- /* I18N: gedcom tag DIV */
- return I18N::translate('Divorce');
- case 'DIVF':
- /* I18N: gedcom tag DIVF */
- return I18N::translate('Divorce filed');
- case 'DSCR':
- /* I18N: gedcom tag DSCR */
- return I18N::translate('Description');
- case 'EDUC':
- /* I18N: gedcom tag EDUC */
- return I18N::translate('Education');
- case 'EDUC:AGNC':
- return I18N::translate('School or college');
- case 'EMAI':
- case 'EMAL':
- case 'EMAIL':
- /* I18N: gedcom tag EMAIL */
- return I18N::translate('Email address');
- case 'EMIG':
- /* I18N: gedcom tag EMIG */
- return I18N::translate('Emigration');
- case 'EMIG:DATE':
- return I18N::translate('Date of emigration');
- case 'EMIG:PLAC':
- return I18N::translate('Place of emigration');
- case 'ENDL':
- /* I18N: gedcom tag ENDL. LDS = Church of Latter Day Saints. */
- return I18N::translate('LDS endowment');
- case 'ENDL:DATE':
- /* I18N: LDS = Church of Latter Day Saints. */
- return I18N::translate('Date of LDS endowment');
- case 'ENDL:PLAC':
- /* I18N: LDS = Church of Latter Day Saints. */
- return I18N::translate('Place of LDS endowment');
- case 'ENGA':
- /* I18N: gedcom tag ENGA */
- return I18N::translate('Engagement');
- case 'ENGA:DATE':
- return I18N::translate('Date of engagement');
- case 'ENGA:PLAC':
- return I18N::translate('Place of engagement');
- case 'EVEN':
- /* I18N: gedcom tag EVEN */
- return I18N::translate('Event');
- case 'EVEN:DATE':
- return I18N::translate('Date of event');
- case 'EVEN:PLAC':
- return I18N::translate('Place of event');
- case 'EVEN:TYPE':
- return I18N::translate('Type of event');
- case 'FACT':
- /* I18N: gedcom tag FACT */
- return I18N::translate('Fact');
- case 'FACT:TYPE':
- return I18N::translate('Type of fact');
- case 'FAM':
- /* I18N: gedcom tag FAM */
- return I18N::translate('Family');
- case 'FAMC':
- /* I18N: gedcom tag FAMC */
- return I18N::translate('Family as a child');
- case 'FAMF':
- /* I18N: gedcom tag FAMF */
- return I18N::translate('Family file');
- case 'FAMS':
- /* I18N: gedcom tag FAMS */
- return I18N::translate('Family as a spouse');
- case 'FAX':
- /* I18N: gedcom tag FAX */
- return I18N::translate('Fax');
- case 'FCOM':
- /* I18N: gedcom tag FCOM */
- return I18N::translate('First communion');
- case 'FCOM:DATE':
- return I18N::translate('Date of first communion');
- case 'FCOM:PLAC':
- return I18N::translate('Place of first communion');
- case 'FILE':
- /* I18N: gedcom tag FILE */
- return I18N::translate('Filename');
- case 'FONE':
- /* I18N: gedcom tag FONE */
- return I18N::translate('Phonetic');
- case 'FORM':
- /* I18N: gedcom tag FORM */
- return I18N::translate('Format');
- case 'GEDC':
- /* I18N: gedcom tag GEDC */
- return I18N::translate('GEDCOM file');
- case 'GIVN':
- /* I18N: gedcom tag GIVN */
- return I18N::translate('Given names');
- case 'GRAD':
- /* I18N: gedcom tag GRAD */
- return I18N::translate('Graduation');
- case 'HEAD':
- /* I18N: gedcom tag HEAD */
- return I18N::translate('Header');
- case 'HUSB':
- /* I18N: gedcom tag HUSB */
- return I18N::translate('Husband');
- case 'IDNO':
- /* I18N: gedcom tag IDNO */
- return I18N::translate('Identification number');
- case 'IMMI':
- /* I18N: gedcom tag IMMI */
- return I18N::translate('Immigration');
- case 'IMMI:DATE':
- return I18N::translate('Date of immigration');
- case 'IMMI:PLAC':
- return I18N::translate('Place of immigration');
- case 'INDI':
- /* I18N: gedcom tag INDI */
- return I18N::translate('Individual');
- case 'INFL':
- /* I18N: gedcom tag INFL */
- return I18N::translate('Infant');
- case 'LANG':
- /* I18N: gedcom tag LANG */
- return I18N::translate('Language');
- case 'LATI':
- /* I18N: gedcom tag LATI */
- return I18N::translate('Latitude');
- case 'LEGA':
- /* I18N: gedcom tag LEGA */
- return I18N::translate('Legatee');
- case 'LONG':
- /* I18N: gedcom tag LONG */
- return I18N::translate('Longitude');
- case 'MAP':
- /* I18N: gedcom tag MAP */
- return I18N::translate('Coordinates');
- case 'MARB':
- /* I18N: gedcom tag MARB */
- return I18N::translate('Marriage banns');
- case 'MARB:DATE':
- return I18N::translate('Date of marriage banns');
- case 'MARB:PLAC':
- return I18N::translate('Place of marriage banns');
- case 'MARC':
- /* I18N: gedcom tag MARC */
- return I18N::translate('Marriage contract');
- case 'MARL':
- /* I18N: gedcom tag MARL */
- return I18N::translate('Marriage license');
- case 'MARR':
- /* I18N: gedcom tag MARR */
- return I18N::translate('Marriage');
- case 'MARR:DATE':
- return I18N::translate('Date of marriage');
- case 'MARR:PLAC':
- return I18N::translate('Place of marriage');
- case 'MARR_CIVIL':
- return I18N::translate('Civil marriage');
- case 'MARR_PARTNERS':
- return I18N::translate('Registered partnership');
- case 'MARR_RELIGIOUS':
- return I18N::translate('Religious marriage');
- case 'MARR_UNKNOWN':
- return I18N::translate('Marriage type unknown');
- case 'MARS':
- /* I18N: gedcom tag MARS */
- return I18N::translate('Marriage settlement');
- case 'MEDI':
- /* I18N: gedcom tag MEDI */
- return I18N::translate('Media type');
- case 'NAME':
- /* I18N: gedcom tag NAME */
- return I18N::translate('Name');
- case 'NAME:FONE':
- return I18N::translate('Phonetic name');
- case 'NAME:_HEB':
- return I18N::translate('Name in Hebrew');
- case 'NATI':
- /* I18N: gedcom tag NATI */
- return I18N::translate('Nationality');
- case 'NATU':
- /* I18N: gedcom tag NATU */
- return I18N::translate('Naturalization');
- case 'NATU:DATE':
- return I18N::translate('Date of naturalization');
- case 'NATU:PLAC':
- return I18N::translate('Place of naturalization');
- case 'NCHI':
- /* I18N: gedcom tag NCHI */
- return I18N::translate('Number of children');
- case 'NICK':
- /* I18N: gedcom tag NICK */
- return I18N::translate('Nickname');
- case 'NMR':
- /* I18N: gedcom tag NMR */
- return I18N::translate('Number of marriages');
- case 'NOTE':
- /* I18N: gedcom tag NOTE */
- return I18N::translate('Note');
- case 'NPFX':
- /* I18N: gedcom tag NPFX */
- return I18N::translate('Name prefix');
- case 'NSFX':
- /* I18N: gedcom tag NSFX */
- return I18N::translate('Name suffix');
- case 'OBJE':
- /* I18N: gedcom tag OBJE */
- return I18N::translate('Media object');
- case 'OCCU':
- /* I18N: gedcom tag OCCU */
- return I18N::translate('Occupation');
- case 'OCCU:AGNC':
- return I18N::translate('Employer');
- case 'ORDI':
- /* I18N: gedcom tag ORDI */
- return I18N::translate('Ordinance');
- case 'ORDN':
- /* I18N: gedcom tag ORDN */
- return I18N::translate('Ordination');
- case 'ORDN:AGNC':
- return I18N::translate('Religious institution');
- case 'ORDN:DATE':
- return I18N::translate('Date of ordination');
- case 'ORDN:PLAC':
- return I18N::translate('Place of ordination');
- case 'PAGE':
- /* I18N: gedcom tag PAGE */
- return I18N::translate('Citation details');
- case 'PEDI':
- /* I18N: gedcom tag PEDI */
- return I18N::translate('Relationship to parents');
- case 'PHON':
- /* I18N: gedcom tag PHON */
- return I18N::translate('Phone');
- case '_PLACE': // Family Tree Builder uses OBJE:_PLACE
- case 'PLAC':
- /* I18N: gedcom tag PLAC */
- return I18N::translate('Place');
- case 'PLAC:FONE':
- return I18N::translate('Phonetic place');
- case 'PLAC:ROMN':
- return I18N::translate('Romanized place');
- case 'PLAC:_HEB':
- return I18N::translate('Place in Hebrew');
- case 'POST':
- /* I18N: gedcom tag POST */
- return I18N::translate('Postal code');
- case 'PROB':
- /* I18N: gedcom tag PROB */
- return I18N::translate('Probate');
- case 'PROP':
- /* I18N: gedcom tag PROP */
- return I18N::translate('Property');
- case 'PUBL':
- /* I18N: gedcom tag PUBL */
- return I18N::translate('Publication');
- case 'QUAY':
- /* I18N: gedcom tag QUAY */
- return I18N::translate('Quality of data');
- case 'REFN':
- /* I18N: gedcom tag REFN */
- return I18N::translate('Reference number');
- case 'RELA':
- /* I18N: gedcom tag RELA */
- return I18N::translate('Relationship');
- case 'RELI':
- /* I18N: gedcom tag RELI */
- return I18N::translate('Religion');
- case 'REPO':
- /* I18N: gedcom tag REPO */
- return I18N::translate('Repository');
- case 'REPO:NAME':
- return I18N::translateContext('Repository', 'Name');
- case 'RESI':
- /* I18N: gedcom tag RESI */
- return I18N::translate('Residence');
- case 'RESI:DATE':
- return I18N::translate('Date of residence');
- case 'RESI:PLAC':
- return I18N::translate('Place of residence');
- case 'RESN':
- /* I18N: gedcom tag RESN */
- return I18N::translate('Restriction');
- case 'RETI':
- /* I18N: gedcom tag RETI */
- return I18N::translate('Retirement');
- case 'RETI:AGNC':
- return I18N::translate('Employer');
- case 'RFN':
- /* I18N: gedcom tag RFN */
- return I18N::translate('Record file number');
- case '_PHOTO_RIN':
- // Family Tree Builder uses "0 OBJE/1 _PHOTO_RIN"
- // no break
- case '_PRIN':// Family Tree Builder uses "0 _ALBUM/1 _PHOTO/2 _PRIN"
- case 'RIN':
- /* I18N: gedcom tag RIN */
- return I18N::translate('Record ID number');
- case 'ROLE':
- /* I18N: gedcom tag ROLE */
- return I18N::translate('Role');
- case 'ROMN':
- /* I18N: gedcom tag ROMN */
- return I18N::translate('Romanized');
- case 'SERV':
- /* I18N: gedcom tag SERV */
- return I18N::translate('Remote server');
- case 'SEX':
- /* I18N: gedcom tag SEX */
- return I18N::translate('Gender');
- case 'SHARED_NOTE':
- return I18N::translate('Shared note');
- case 'SLGC':
- /* I18N: gedcom tag SLGC. LDS = Church of Latter Day Saints. */
- return I18N::translate('LDS child sealing');
- case 'SLGC:DATE':
- /* I18N: LDS = Church of Latter Day Saints. */
- return I18N::translate('Date of LDS child sealing');
- case 'SLGC:PLAC':
- /* I18N: LDS = Church of Latter Day Saints. */
- return I18N::translate('Place of LDS child sealing');
- case 'SLGS':
- /* I18N: gedcom tag SLGS. LDS = Church of Latter Day Saints. */
- return I18N::translate('LDS spouse sealing');
- case 'SOUR':
- /* I18N: gedcom tag SOUR */
- return I18N::translate('Source');
- case 'SPFX':
- /* I18N: gedcom tag SPFX */
- return I18N::translate('Surname prefix');
- case 'SSN':
- /* I18N: gedcom tag SSN */
- return I18N::translate('Social security number');
- case 'STAE':
- /* I18N: gedcom tag STAE */
- return I18N::translate('State');
- case 'STAT':
- /* I18N: gedcom tag STAT */
- return I18N::translate('Status');
- case 'STAT:DATE':
- return I18N::translate('Status change date');
- case 'SUBM':
- /* I18N: gedcom tag SUBM */
- return I18N::translate('Submitter');
- case 'SUBN':
- /* I18N: gedcom tag SUBN */
- return I18N::translate('Submission');
- case 'SURN':
- /* I18N: gedcom tag SURN */
- return I18N::translate('Surname');
- case 'TEMP':
- /* I18N: gedcom tag TEMP */
- return I18N::translate('Temple');
- case 'TEXT':
- /* I18N: gedcom tag TEXT */
- return I18N::translate('Text');
- case 'TIME':
- /* I18N: gedcom tag TIME */
- return I18N::translate('Time');
- case 'TITL':
- /* I18N: gedcom tag TITL */
- return I18N::translate('Title');
- case 'TITL:FONE':
- return I18N::translate('Phonetic title');
- case 'TITL:ROMN':
- return I18N::translate('Romanized title');
- case 'TITL:_HEB':
- return I18N::translate('Title in Hebrew');
- case 'TRLR':
- /* I18N: gedcom tag TRLR */
- return I18N::translate('Trailer');
- case 'TYPE':
- /* I18N: gedcom tag TYPE */
- return I18N::translate('Type');
- case 'URL':
- /* I18N: gedcom tag URL (A web address / URL) */
- return I18N::translate('URL');
- case 'VERS':
- /* I18N: gedcom tag VERS */
- return I18N::translate('Version');
- case 'WIFE':
- /* I18N: gedcom tag WIFE */
- return I18N::translate('Wife');
- case 'WILL':
- /* I18N: gedcom tag WILL */
- return I18N::translate('Will');
- case 'WWW':
- /* I18N: gedcom tag WWW (A web address / URL) */
- return I18N::translate('URL');
-
- case '_ADPF':
- /* I18N: gedcom tag _ADPF */
- return I18N::translate('Adopted by father');
-
- case '_ADPM':
- /* I18N: gedcom tag _ADPM */
- return I18N::translate('Adopted by mother');
-
- case '_AKA':
- case '_AKAN':
- /* I18N: gedcom tag _AKA */
- return I18N::translate('Also known as');
-
- case '_ALBUM':
- // Family Tree Builder uses OBJE:_ALBUM
- /* I18N: gedcom tag _ALBUM */
- return I18N::translate('Album');
- case '_ASSO':
- /* I18N: gedcom tag _ASSO */
- return I18N::translate('Associate');
-
- case '_BIBL':
- /* I18N: gedcom tag _BIBL */
- return I18N::translate('Bibliography');
-
- case '_BRTM':
- /* I18N: gedcom tag _BRTM */
- return I18N::translate('Brit milah');
- case '_BRTM:DATE':
- return I18N::translate('Date of brit milah');
- case '_BRTM:PLAC':
- return I18N::translate('Place of brit milah');
-
- case '_COML':
- /* I18N: gedcom tag _COML */
- return I18N::translate('Common law marriage');
-
- case '_DBID':
- /* I18N: gedcom tag _DBID */
- return I18N::translate('Linked database ID');
-
- case '_DEG':
- /* I18N: gedcom tag _DEG */
- return I18N::translate('Degree');
- case '_DETS':
- /* I18N: gedcom tag _DETS */
- return I18N::translate('Death of one spouse');
- case '_DNA':
- /* I18N: gedcom tag _DNA (from FTM 2010) */
- return I18N::translate('DNA markers');
- case '_EMAIL':
- /* I18N: gedcom tag _EMAIL */
- return I18N::translate('Email address');
- case '_EYEC':
- /* I18N: gedcom tag _EYEC */
- return I18N::translate('Eye color');
- case '_FA1':
- return I18N::translate('Fact 1');
- case '_FA2':
- return I18N::translate('Fact 2');
- case '_FA3':
- return I18N::translate('Fact 3');
- case '_FA4':
- return I18N::translate('Fact 4');
- case '_FA5':
- return I18N::translate('Fact 5');
- case '_FA6':
- return I18N::translate('Fact 6');
- case '_FA7':
- return I18N::translate('Fact 7');
- case '_FA8':
- return I18N::translate('Fact 8');
- case '_FA9':
- return I18N::translate('Fact 9');
- case '_FA10':
- return I18N::translate('Fact 10');
- case '_FA11':
- return I18N::translate('Fact 11');
- case '_FA12':
- return I18N::translate('Fact 12');
- case '_FA13':
- return I18N::translate('Fact 13');
- case '_FNRL':
- /* I18N: gedcom tag _FNRL */
- return I18N::translate('Funeral');
- case '_FREL':
- /* I18N: gedcom tag _FREL */
- return I18N::translate('Relationship to father');
- case '_GEDF':
- /* I18N: gedcom tag _GEDF */
- return I18N::translate('GEDCOM file');
- case '_GODP':
- /* I18N: gedcom tag _GODP */
- return I18N::translate('Godparent');
- case '_GOV':
- /* I18N: gedcom tag _GOV - see https://gov.genealogy.net */
- return I18N::translate('GOV identifier');
- case '_HAIR':
- /* I18N: gedcom tag _HAIR */
- return I18N::translate('Hair color');
- case '_HEB':
- /* I18N: gedcom tag _HEB */
- return I18N::translate('Hebrew');
- case '_HEIG':
- /* I18N: gedcom tag _HEIG */
- return I18N::translate('Height');
- case '_HNM':
- /* I18N: gedcom tag _HNM */
- return I18N::translate('Hebrew name');
- case '_HOL':
- /* I18N: gedcom tag _HOL */
- return I18N::translate('Holocaust');
-
- case '_INTE':
- /* I18N: gedcom tag _INTE */
- return I18N::translate('Interment');
-
- case '_LOC':
- /* I18N: gedcom tag _LOC */
- return I18N::translate('Location');
- case '_MARI':
- /* I18N: gedcom tag _MARI */
- return I18N::translate('Marriage intention');
- case '_MARNM':
- /* I18N: gedcom tag _MARNM */
- return I18N::translate('Married name');
- case '_PRIM':
- /* I18N: gedcom tag _PRIM */
- return I18N::translate('Highlighted image');
- case '_MARNM_SURN':
- return I18N::translate('Married surname');
-
- case '_MBON':
- /* I18N: gedcom tag _MBON */
- return I18N::translate('Marriage bond');
- case '_MDCL':
- /* I18N: gedcom tag _MDCL */
- return I18N::translate('Medical');
- case '_MEDC':
- /* I18N: gedcom tag _MEDC */
- return I18N::translate('Medical condition');
- case '_MEND':
- /* I18N: gedcom tag _MEND */
- return I18N::translate('Marriage ending status');
- case '_MILI':
- /* I18N: gedcom tag _MILI */
- return I18N::translate('Military');
- case '_MILT':
- /* I18N: gedcom tag _MILT */
- return I18N::translate('Military service');
- case '_MREL':
- /* I18N: gedcom tag _MREL */
- return I18N::translate('Relationship to mother');
- case '_MSTAT':
- /* I18N: gedcom tag _MSTAT */
- return I18N::translate('Marriage beginning status');
- case '_NAME':
- /* I18N: gedcom tag _NAME */
- return I18N::translate('Mailing name');
- case '_NAMS':
- /* I18N: gedcom tag _NAMS */
- return I18N::translate('Namesake');
- case '_NLIV':
- /* I18N: gedcom tag _NLIV */
- return I18N::translate('Not living');
- case '_NMAR':
- /* I18N: gedcom tag _NMAR */
- return I18N::translate('Never married');
-
- case '_NMR':
- /* I18N: gedcom tag _NMR */
- return I18N::translate('Not married');
-
- case '_PHOTO':
- // Family Tree Builder uses "0 _ALBUM/1_PHOTO"
- return I18N::translate('Photo');
- case '_WT_USER':
- return I18N::translate('by');
- case '_PRMN':
- /* I18N: gedcom tag _PRMN */
- return I18N::translate('Permanent number');
- case '_RNAME':
- return I18N::translate('Religious name');
-
- case '_SCBK':
- /* I18N: gedcom tag _SCBK */
- return I18N::translate('Scrapbook');
- case '_SEPR':
- /* I18N: gedcom tag _SEPR */
- return I18N::translate('Separation');
- case '_SSHOW':
- /* I18N: gedcom tag _SSHOW */
- return I18N::translate('Slide show');
- case '_STAT':
- /* I18N: gedcom tag _STAT */
- return I18N::translate('Marriage status');
- case '_SUBQ':
- /* I18N: gedcom tag _SUBQ */
- return I18N::translate('Short version');
- case '_TODO':
- /* I18N: gedcom tag _TODO */
- return I18N::translate('Research task');
- case '_TYPE':
- /* I18N: gedcom tag _TYPE */
- return I18N::translate('Media type');
- case '_UID':
- /* I18N: gedcom tag _UID */
- return I18N::translate('Unique identifier');
- case '_URL':
- /* I18N: gedcom tag _URL */
- return I18N::translate('URL');
- case '_WEIG':
- /* I18N: gedcom tag _WEIG */
- return I18N::translate('Weight');
- case '_WITN':
- /* I18N: gedcom tag _WITN */
- return I18N::translate('Witness');
- case '_WT_OBJE_SORT':
- /* I18N: gedcom tag _WT_OBJE_SORT */
- return I18N::translate('Re-order media');
- case '_YART':
- /* I18N: gedcom tag _YART - A yahrzeit is a special anniversary of death in the Hebrew faith/calendar. */
- return I18N::translate('Yahrzeit');
- case '_FILESIZE': // Family Tree Builder uses OBJE:_FILESIZE
- case '__FILE_SIZE__':
- // This pseudo-tag is generated internally to present information about a media object
- return I18N::translate('File size');
- case '__IMAGE_SIZE__':
- // This pseudo-tag is generated internally to present information about a media object
- return I18N::translate('Image dimensions');
- default:
- // If no specialisation exists (e.g. DEAT:CAUS), then look for the general (CAUS)
- if (str_contains($tag, ':')) {
- [, $tag] = explode(':', $tag, 2);
-
- return self::getLabel($tag);
- }
-
- // Still no translation? Highlight this as an error
- return '<span class="error" title="' . I18N::translate('Unrecognized GEDCOM code') . '">' . e($tag) . '</span>';
- }
+ return Registry::elementFactory()->make($tag)->label();
}
/**
diff --git a/app/Http/RequestHandlers/AddChildToFamilyAction.php b/app/Http/RequestHandlers/AddChildToFamilyAction.php
index da916d5eb0..76afb028c9 100644
--- a/app/Http/RequestHandlers/AddChildToFamilyAction.php
+++ b/app/Http/RequestHandlers/AddChildToFamilyAction.php
@@ -30,7 +30,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
-use function preg_match_all;
+use function is_string;
use function redirect;
/**
@@ -61,78 +61,25 @@ class AddChildToFamilyAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
-
- $family = Registry::familyFactory()->make($xref, $tree);
- $family = Auth::checkFamilyAccess($family, true);
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$params = (array) $request->getParsedBody();
- $PEDI = $params['PEDI'];
- $keep_chan = (bool) ($params['keep_chan'] ?? false);
-
- $this->gedcom_edit_service->glevels = $params['glevels'] ?? [];
- $this->gedcom_edit_service->tag = $params['tag'] ?? [];
- $this->gedcom_edit_service->text = $params['text'] ?? [];
- $this->gedcom_edit_service->islink = $params['islink'] ?? [];
-
- $this->gedcom_edit_service->splitSource();
- $gedrec = '0 @@ INDI';
- $gedrec .= $this->gedcom_edit_service->addNewName($request, $tree);
- $gedrec .= $this->gedcom_edit_service->addNewSex($request);
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $gedrec .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
-
- switch ($PEDI) {
- case '':
- $gedrec .= "\n1 FAMC @$xref@";
- break;
- case 'adopted':
- $gedrec .= "\n1 FAMC @$xref@\n2 PEDI $PEDI\n1 ADOP\n2 FAMC @$xref@\n3 ADOP BOTH";
- break;
- case 'sealing':
- $gedrec .= "\n1 FAMC @$xref@\n2 PEDI $PEDI\n1 SLGC\n2 FAMC @$xref@";
- break;
- case 'foster':
- $gedrec .= "\n1 FAMC @$xref@\n2 PEDI $PEDI\n1 EVEN\n2 TYPE $PEDI";
- break;
- default:
- $gedrec .= "\n1 FAMC @$xref@\n2 PEDI $PEDI";
- break;
- }
+ $family = Registry::familyFactory()->make($xref, $tree);
+ $family = Auth::checkFamilyAccess($family, true);
- if ($params['SOUR_INDI'] ?? false) {
- $gedrec = $this->gedcom_edit_service->handleUpdates($gedrec);
- } else {
- $gedrec = $this->gedcom_edit_service->updateRest($gedrec);
- }
+ $levels = $params['ilevels'] ?? [];
+ $tags = $params['itags'] ?? [];
+ $values = $params['ivalues'] ?? [];
// Create the new child
- $new_child = $tree->createIndividual($gedrec);
-
- // Insert new child at the right place
- $done = false;
- foreach ($family->facts(['CHIL']) as $fact) {
- $old_child = $fact->target();
- if ($old_child instanceof Individual && Date::compare($new_child->getEstimatedBirthDate(), $old_child->getEstimatedBirthDate()) < 0) {
- // Insert before this child
- $family->updateFact($fact->id(), '1 CHIL @' . $new_child->xref() . "@\n" . $fact->gedcom(), !$keep_chan);
- $done = true;
- break;
- }
- }
- if (!$done) {
- // Append child at end
- $family->createFact('1 CHIL @' . $new_child->xref() . '@', !$keep_chan);
- }
+ $gedcom = "0 @@ INDI\n1 FAMC @" . $xref . "@\n" . $this->gedcom_edit_service->editLinesToGedcom('INDI', $levels, $tags, $values);
+ $child = $tree->createIndividual($gedcom);
- if (($params['goto'] ?? '') === 'new') {
- return redirect($new_child->url());
- }
+ // Link the child to the family
+ $family->createFact('1 CHIL @' . $child->xref() . '@', false);
- return redirect($family->url());
+ return redirect($params['url'] ?? $child->url());
}
}
diff --git a/app/Http/RequestHandlers/AddChildToFamilyPage.php b/app/Http/RequestHandlers/AddChildToFamilyPage.php
index ccca8d1c3e..f1eb504726 100644
--- a/app/Http/RequestHandlers/AddChildToFamilyPage.php
+++ b/app/Http/RequestHandlers/AddChildToFamilyPage.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
@@ -29,6 +30,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
+use function route;
/**
* Add a new child to a family.
@@ -47,32 +50,42 @@ class AddChildToFamilyPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
+
+ $sex = $request->getAttribute('sex');
+ assert(is_string($sex));
$family = Registry::familyFactory()->make($xref, $tree);
$family = Auth::checkFamilyAccess($family, true);
- $gender = $request->getQueryParams()['gender'];
+ // Create a dummy individual, so that we can create new/empty facts.
+ $element = Registry::elementFactory()->make('INDI:NAME');
+ $dummy = Registry::individualFactory()->new('', '0 @@ INDI', null, $tree);
+ $facts = [
+ 'i' => [
+ new Fact('1 SEX ' . $sex, $dummy, ''),
+ new Fact('1 NAME ' . $element->default($tree), $dummy, ''),
+ new Fact('1 BIRT', $dummy, ''),
+ new Fact('1 DEAT', $dummy, ''),
+ ],
+ ];
- $subtitles = [
+ $titles = [
'M' => I18N::translate('Add a son'),
'F' => I18N::translate('Add a daughter'),
'U' => I18N::translate('Add a child'),
];
- $subtitle = $subtitles[$gender] ?? $subtitles['U'];
-
- $title = $family->fullName() . ' - ' . $subtitle;
+ $title = $titles[$sex] ?? $titles['U'];
return $this->viewResponse('edit/new-individual', [
- 'next_action' => AddChildToFamilyAction::class,
- 'tree' => $tree,
- 'title' => $title,
- 'individual' => null,
- 'family' => $family,
- 'name_fact' => null,
- 'famtag' => 'CHIL',
- 'gender' => $gender,
+ 'cancel_url' => $family->url(),
+ 'facts' => $facts,
+ 'post_url' => route(AddChildToFamilyAction::class, ['tree' => $tree->name(), 'xref' => $xref]),
+ 'title' => $family->fullName() . ' - ' . $title,
+ 'tree' => $tree,
+ 'url' => $request->getQueryParams()['url'] ?? $family->url(),
]);
}
}
diff --git a/app/Http/RequestHandlers/AddChildToIndividualAction.php b/app/Http/RequestHandlers/AddChildToIndividualAction.php
index 2d87d950ac..016367a58e 100644
--- a/app/Http/RequestHandlers/AddChildToIndividualAction.php
+++ b/app/Http/RequestHandlers/AddChildToIndividualAction.php
@@ -28,7 +28,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
-use function preg_match_all;
+use function is_string;
use function redirect;
/**
@@ -59,77 +59,33 @@ class AddChildToIndividualAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
+
+ $params = (array) $request->getParsedBody();
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
- $params = (array) $request->getParsedBody();
+ $levels = $params['ilevels'] ?? [];
+ $tags = $params['itags'] ?? [];
+ $values = $params['ivalues'] ?? [];
- $PEDI = $params['PEDI'];
+ // Create the new child
+ $gedcom = "0 @@ INDI\n" . $this->gedcom_edit_service->editLinesToGedcom('INDI', $levels, $tags, $values);
+ $child = $tree->createIndividual($gedcom);
- $this->gedcom_edit_service->glevels = $params['glevels'] ?? [];
- $this->gedcom_edit_service->tag = $params['tag'] ?? [];
- $this->gedcom_edit_service->text = $params['text'] ?? [];
- $this->gedcom_edit_service->islink = $params['islink'] ?? [];
-
- // Create a family
- if ($individual->sex() === 'F') {
- $gedcom = "0 @@ FAM\n1 WIFE @" . $individual->xref() . '@';
- } else {
- $gedcom = "0 @@ FAM\n1 HUSB @" . $individual->xref() . '@';
- }
+ // Create a new family
+ $link = $child->sex() === 'F' ? 'WIFE' : 'HUSB';
+ $gedcom = "0 @@ FAM\n1 " . $link . " @" . $individual->xref() . "@\n1 CHIL @" . $child->xref() . '@';
$family = $tree->createFamily($gedcom);
- // Link the parent to the family
- $individual->createFact('1 FAMS @' . $family->xref() . '@', true);
-
- // Create a child
- $this->gedcom_edit_service->splitSource(); // separate SOUR record from the rest
-
- $gedcom = '0 @@ INDI';
- $gedcom .= $this->gedcom_edit_service->addNewName($request, $tree);
- $gedcom .= $this->gedcom_edit_service->addNewSex($request);
-
- $fam_xref = $family->xref();
- switch ($PEDI) {
- case '':
- $gedcom .= "\n1 FAMC @$fam_xref@";
- break;
- case 'adopted':
- $gedcom .= "\n1 FAMC @$fam_xref@\n2 PEDI $PEDI\n1 ADOP\n2 FAMC @$fam_xref@\n3 ADOP BOTH";
- break;
- case 'sealing':
- $gedcom .= "\n1 FAMC @$fam_xref@\n2 PEDI $PEDI\n1 SLGC\n2 FAMC @$fam_xref@";
- break;
- case 'foster':
- $gedcom .= "\n1 FAMC @$fam_xref@\n2 PEDI $PEDI\n1 EVEN\n2 TYPE $PEDI";
- break;
- default:
- $gedcom .= "\n1 FAMC @$fam_xref@\n2 PEDI $PEDI";
- break;
- }
-
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $gedcom .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
- if ($params['SOUR_INDI'] ?? false) {
- $gedcom = $this->gedcom_edit_service->handleUpdates($gedcom);
- } else {
- $gedcom = $this->gedcom_edit_service->updateRest($gedcom);
- }
-
- $child = $tree->createIndividual($gedcom);
-
- // Link the family to the child
- $family->createFact('1 CHIL @' . $child->xref() . '@', true);
+ // Link the individual to the family
+ $individual->createFact('1 FAMS @' . $family->xref() . '@', false);
- if (($params['goto'] ?? '') === 'new') {
- return redirect($child->url());
- }
+ // Link the child to the family
+ $child->createFact('1 FAMC @' . $family->xref() . '@', false);
- return redirect($individual->url());
+ return redirect($params['url'] ?? $child->url());
}
}
diff --git a/app/Http/RequestHandlers/AddChildToIndividualPage.php b/app/Http/RequestHandlers/AddChildToIndividualPage.php
index 4f0512fe64..662d165b87 100644
--- a/app/Http/RequestHandlers/AddChildToIndividualPage.php
+++ b/app/Http/RequestHandlers/AddChildToIndividualPage.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
@@ -29,6 +30,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
+use function route;
/**
* Add a new child to an individual, creating a one-parent family.
@@ -47,22 +50,33 @@ class AddChildToIndividualPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
- $title = $individual->fullName() . ' - ' . I18N::translate('Add a child to create a one-parent family');
+ // Create a dummy individual, so that we can create new/empty facts.
+ $element = Registry::elementFactory()->make('INDI:NAME');
+ $dummy = Registry::individualFactory()->new('', '0 @@ INDI', null, $tree);
+ $facts = [
+ 'i' => [
+ new Fact('1 SEX ', $dummy, ''),
+ new Fact('1 NAME ' . $element->default($tree), $dummy, ''),
+ new Fact('1 BIRT', $dummy, ''),
+ new Fact('1 DEAT', $dummy, ''),
+ ],
+ ];
+
+ $title = I18N::translate('Add a child to create a one-parent family');
return $this->viewResponse('edit/new-individual', [
- 'next_action' => AddChildToIndividualAction::class,
- 'tree' => $tree,
- 'title' => $title,
- 'individual' => $individual,
- 'family' => null,
- 'name_fact' => null,
- 'famtag' => 'CHIL',
- 'gender' => 'U',
+ 'cancel_url' => $individual->url(),
+ 'facts' => $facts,
+ 'post_url' => route(AddChildToIndividualAction::class, ['tree' => $tree->name(), 'xref' => $xref]),
+ 'title' => $individual->fullName() . ' - ' . $title,
+ 'tree' => $tree,
+ 'url' => $request->getQueryParams()['url'] ?? $individual->url(),
]);
}
}
diff --git a/app/Http/RequestHandlers/AddName.php b/app/Http/RequestHandlers/AddName.php
deleted file mode 100644
index 03cada10fc..0000000000
--- a/app/Http/RequestHandlers/AddName.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * webtrees: online genealogy
- * Copyright (C) 2021 webtrees development team
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-declare(strict_types=1);
-
-namespace Fisharebest\Webtrees\Http\RequestHandlers;
-
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Http\ViewResponseTrait;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Registry;
-use Fisharebest\Webtrees\Tree;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Server\RequestHandlerInterface;
-
-use function assert;
-
-/**
- * Add a new individual name.
- */
-class AddName implements RequestHandlerInterface
-{
- use ViewResponseTrait;
-
- /**
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function handle(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree);
-
- $xref = $request->getQueryParams()['xref'];
-
- $individual = Registry::individualFactory()->make($xref, $tree);
- $individual = Auth::checkIndividualAccess($individual, true);
-
- $title = $individual->fullName() . ' — ' . I18N::translate('Add a name');
-
- return $this->viewResponse('edit/new-individual', [
- 'next_action' => EditFactAction::class,
- 'tree' => $tree,
- 'title' => $title,
- 'individual' => $individual,
- 'family' => null,
- 'name_fact' => null,
- 'famtag' => '',
- 'gender' => $individual->sex(),
- ]);
- }
-}
diff --git a/app/Http/RequestHandlers/AddNewFact.php b/app/Http/RequestHandlers/AddNewFact.php
index 2576d63aed..ad3099ffa6 100644
--- a/app/Http/RequestHandlers/AddNewFact.php
+++ b/app/Http/RequestHandlers/AddNewFact.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Tree;
@@ -29,6 +30,7 @@ use Psr\Http\Server\RequestHandlerInterface;
use function assert;
use function is_string;
+use function trim;
/**
* Add a new fact.
@@ -50,20 +52,19 @@ class AddNewFact implements RequestHandlerInterface
$xref = $request->getAttribute('xref');
assert(is_string($xref));
- $fact = $request->getAttribute('fact');
+ $subtag = $request->getAttribute('fact');
+ $record = Registry::gedcomRecordFactory()->make($xref, $tree);
+ $record = Auth::checkRecordAccess($record, true);
+ $element = Registry::elementFactory()->make($record->tag() . ':' . $subtag);
+ $title = $record->fullName() . ' - ' . $element->label();
+ $fact = new Fact(trim('1 ' . $subtag . ' ' . $element->default($tree)), $record, 'new');
- $record = Registry::gedcomRecordFactory()->make($xref, $tree);
- $record = Auth::checkRecordAccess($record, true);
-
- $element = Registry::elementFactory()->make($record->tag() . ':' . $fact);
-
- $title = $record->fullName() . ' - ' . $element->label();
-
- return $this->viewResponse('edit/add-fact', [
- 'fact' => $fact,
- 'record' => $record,
- 'title' => $title,
- 'tree' => $tree,
+ return $this->viewResponse('edit/edit-fact', [
+ 'can_edit_raw' => false,
+ 'fact' => $fact,
+ 'title' => $title,
+ 'tree' => $tree,
+ 'url' => $record->url(),
]);
}
}
diff --git a/app/Http/RequestHandlers/AddParentToIndividualAction.php b/app/Http/RequestHandlers/AddParentToIndividualAction.php
index 1c22a4ea51..8a2f2100d4 100644
--- a/app/Http/RequestHandlers/AddParentToIndividualAction.php
+++ b/app/Http/RequestHandlers/AddParentToIndividualAction.php
@@ -28,7 +28,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
-use function preg_match_all;
+use function is_string;
use function redirect;
/**
@@ -59,56 +59,33 @@ class AddParentToIndividualAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
+
+ $params = (array) $request->getParsedBody();
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
- $params = (array) $request->getParsedBody();
+ $levels = $params['ilevels'] ?? [];
+ $tags = $params['itags'] ?? [];
+ $values = $params['ivalues'] ?? [];
- $this->gedcom_edit_service->glevels = $params['glevels'] ?? [];
- $this->gedcom_edit_service->tag = $params['tag'] ?? [];
- $this->gedcom_edit_service->text = $params['text'] ?? [];
- $this->gedcom_edit_service->islink = $params['islink'] ?? [];
+ // Create the new parent
+ $gedcom = "0 @@ INDI\n" . $this->gedcom_edit_service->editLinesToGedcom('INDI', $levels, $tags, $values);
+ $parent = $tree->createIndividual($gedcom);
// Create a new family
- $gedcom = "0 @@ FAM\n1 CHIL @" . $individual->xref() . '@';
+ $link = $parent->sex() === 'F' ? 'WIFE' : 'HUSB';
+ $gedcom = "0 @@ FAM\n1 CHIL @" . $individual->xref() . "@\n1 " . $link . ' @' . $parent->xref() . '@';
$family = $tree->createFamily($gedcom);
- // Link the child to the family
- $individual->createFact('1 FAMC @' . $family->xref() . '@', true);
-
- // Create a child
- $this->gedcom_edit_service->splitSource(); // separate SOUR record from the rest
-
- $gedcom = '0 @@ INDI';
- $gedcom .= $this->gedcom_edit_service->addNewName($request, $tree);
- $gedcom .= $this->gedcom_edit_service->addNewSex($request);
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $gedcom .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
- if ($params['SOUR_INDI'] ?? false) {
- $gedcom = $this->gedcom_edit_service->handleUpdates($gedcom);
- } else {
- $gedcom = $this->gedcom_edit_service->updateRest($gedcom);
- }
- $gedcom .= "\n1 FAMS @" . $family->xref() . '@';
-
- $parent = $tree->createIndividual($gedcom);
-
- // Link the family to the child
- if ($parent->sex() === 'F') {
- $family->createFact('1 WIFE @' . $parent->xref() . '@', true);
- } else {
- $family->createFact('1 HUSB @' . $parent->xref() . '@', true);
- }
+ // Link the individual to the family
+ $individual->createFact('1 FAMC @' . $family->xref() . '@', false);
- if (($params['goto'] ?? '') === 'new') {
- return redirect($parent->url());
- }
+ // Link the parent to the family
+ $parent->createFact('1 FAMS @' . $family->xref() . '@', false);
- return redirect($individual->url());
+ return redirect($params['url'] ?? $parent->url());
}
}
diff --git a/app/Http/RequestHandlers/AddParentToIndividualPage.php b/app/Http/RequestHandlers/AddParentToIndividualPage.php
index 35d55a6b4d..d44f29ebea 100644
--- a/app/Http/RequestHandlers/AddParentToIndividualPage.php
+++ b/app/Http/RequestHandlers/AddParentToIndividualPage.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
@@ -29,6 +30,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
+use function route;
/**
* Add a new parent to an individual, creating a one-parent family.
@@ -47,30 +50,40 @@ class AddParentToIndividualPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
+
+ $sex = $request->getAttribute('sex');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
- $gender = $request->getQueryParams()['gender'];
+ // Create a dummy individual, so that we can create new/empty facts.
+ $element = Registry::elementFactory()->make('INDI:NAME');
+ $dummy = Registry::individualFactory()->new('', '0 @@ INDI', null, $tree);
+ $facts = [
+ 'i' => [
+ new Fact('1 SEX ' . $sex, $dummy, ''),
+ new Fact('1 NAME ' . $element->default($tree), $dummy, ''),
+ new Fact('1 BIRT', $dummy, ''),
+ new Fact('1 DEAT', $dummy, ''),
+ ],
+ ];
- if ($gender === 'F') {
- $title = $individual->fullName() . ' - ' . I18N::translate('Add a mother');
- $famtag = 'WIFE';
+ if ($sex === 'F') {
+ $title = I18N::translate('Add a mother');
} else {
- $title = $individual->fullName() . ' - ' . I18N::translate('Add a father');
- $famtag = 'HUSB';
+ $title = I18N::translate('Add a father');
}
return $this->viewResponse('edit/new-individual', [
- 'next_action' => AddParentToIndividualAction::class,
- 'tree' => $tree,
- 'title' => $title,
- 'individual' => $individual,
- 'family' => null,
- 'name_fact' => null,
- 'famtag' => $famtag,
- 'gender' => $gender,
+ 'cancel_url' => $individual->url(),
+ 'facts' => $facts,
+ 'post_url' => route(AddParentToIndividualAction::class, ['tree' => $tree->name(), 'xref' => $xref]),
+ 'title' => $individual->fullName() . ' - ' . $title,
+ 'tree' => $tree,
+ 'url' => $request->getQueryParams()['url'] ?? $individual->url(),
]);
}
}
diff --git a/app/Http/RequestHandlers/AddSpouseToFamilyAction.php b/app/Http/RequestHandlers/AddSpouseToFamilyAction.php
index fe4773ab6c..d1476dfbed 100644
--- a/app/Http/RequestHandlers/AddSpouseToFamilyAction.php
+++ b/app/Http/RequestHandlers/AddSpouseToFamilyAction.php
@@ -20,7 +20,6 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Services\GedcomEditService;
use Fisharebest\Webtrees\Tree;
@@ -29,9 +28,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
-use function preg_match_all;
+use function is_string;
use function redirect;
-use function trim;
/**
* Add a new spouse to a family.
@@ -61,60 +59,42 @@ class AddSpouseToFamilyAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
- $family = Registry::familyFactory()->make($xref, $tree);
- $family = Auth::checkFamilyAccess($family, true);
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$params = (array) $request->getParsedBody();
- $this->gedcom_edit_service->glevels = $params['glevels'] ?? [];
- $this->gedcom_edit_service->tag = $params['tag'] ?? [];
- $this->gedcom_edit_service->text = $params['text'] ?? [];
- $this->gedcom_edit_service->islink = $params['islink'] ?? [];
+ $family = Registry::familyFactory()->make($xref, $tree);
+ $family = Auth::checkFamilyAccess($family, true);
+
+ $levels = $params['ilevels'] ?? [];
+ $tags = $params['itags'] ?? [];
+ $values = $params['ivalues'] ?? [];
// Create the new spouse
- $this->gedcom_edit_service->splitSource(); // separate SOUR record from the rest
+ $gedcom = "0 @@ INDI\n1 FAMS @" . $family->xref() . "@\n" . $this->gedcom_edit_service->editLinesToGedcom('INDI', $levels, $tags, $values);
+ $spouse = $tree->createIndividual($gedcom);
- $gedrec = '0 @@ INDI';
- $gedrec .= $this->gedcom_edit_service->addNewName($request, $tree);
- $gedrec .= $this->gedcom_edit_service->addNewSex($request);
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $gedrec .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
+ // Link the spouse to the family
+ $husb = $family->facts(['HUSB'], false, null, true)->first();
+ $wife = $family->facts(['WIFE'], false, null, true)->first();
- if ($params['SOUR_INDI'] ?? false) {
- $gedrec = $this->gedcom_edit_service->handleUpdates($gedrec);
+ if ($husb === null && $spouse->sex() === 'M') {
+ $link = 'HUSB';
+ } elseif ($wife === null && $spouse->sex() === 'F') {
+ $link = 'WIFE';
+ } elseif ($husb === null) {
+ $link = 'HUSB';
+ } elseif ($wife === null) {
+ $link = 'WIFE';
} else {
- $gedrec = $this->gedcom_edit_service->updateRest($gedrec);
+ // Family already has husband and wife
+ return redirect($family->url());
}
- $gedrec .= "\n1 FAMS @" . $family->xref() . '@';
- $spouse = $tree->createIndividual($gedrec);
- // Update the existing family - add marriage, etc
- if ($family->facts(['HUSB'])->first() instanceof Fact) {
- $family->createFact('1 WIFE @' . $spouse->xref() . '@', true);
- } else {
- $family->createFact('1 HUSB @' . $spouse->xref() . '@', true);
- }
- $famrec = '';
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $famrec .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
- if ($params['SOUR_FAM'] ?? false) {
- $famrec = $this->gedcom_edit_service->handleUpdates($famrec);
- } else {
- $famrec = $this->gedcom_edit_service->updateRest($famrec);
- }
- $family->createFact(trim($famrec), true); // trim leading \n
-
- if (($params['goto'] ?? '') === 'new') {
- return redirect($spouse->url());
- }
+ // Link the spouse to the family
+ $family->createFact('1 ' . $link . ' @' . $spouse->xref() . '@', false);
- return redirect($family->url());
+ return redirect($params['url'] ?? $spouse->url());
}
}
diff --git a/app/Http/RequestHandlers/AddSpouseToFamilyPage.php b/app/Http/RequestHandlers/AddSpouseToFamilyPage.php
index aefc9b9b53..9086fa6e69 100644
--- a/app/Http/RequestHandlers/AddSpouseToFamilyPage.php
+++ b/app/Http/RequestHandlers/AddSpouseToFamilyPage.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
@@ -29,6 +30,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
+use function route;
/**
* Add a new spouse to a family.
@@ -47,28 +50,44 @@ class AddSpouseToFamilyPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
- $famtag = $request->getQueryParams()['famtag'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
+
+ $sex = $request->getAttribute('sex');
+ assert(is_string($sex));
+
$family = Registry::familyFactory()->make($xref, $tree);
$family = Auth::checkFamilyAccess($family, true);
- if ($famtag === 'WIFE') {
- $title = I18N::translate('Add a wife');
- $gender = 'F';
+ // Create a dummy individual, so that we can create new/empty facts.
+ $element = Registry::elementFactory()->make('INDI:NAME');
+ $dummyi = Registry::individualFactory()->new('', '0 @@ INDI', null, $tree);
+ $dummyf = Registry::familyFactory()->new('', '0 @@ FAM', null, $tree);
+ $facts = [
+ 'i' => [
+ new Fact('1 SEX ' . $sex, $dummyi, ''),
+ new Fact('1 NAME ' . $element->default($tree), $dummyi, ''),
+ new Fact('1 BIRT', $dummyi, ''),
+ new Fact('1 DEAT', $dummyi, ''),
+ ],
+ 'f' => [
+ new Fact('1 MARR', $dummyf, ''),
+ ],
+ ];
+
+ if ($sex === 'F') {
+ $title = I18N::translate('Add a wife');
} else {
- $title = I18N::translate('Add a husband');
- $gender = 'M';
+ $title = I18N::translate('Add a husband');
}
return $this->viewResponse('edit/new-individual', [
- 'next_action' => AddSpouseToFamilyAction::class,
- 'tree' => $tree,
+ 'cancel_url' => $family->url(),
+ 'facts' => $facts,
+ 'post_url' => route(AddSpouseToFamilyAction::class, ['tree' => $tree->name(), 'xref' => $xref]),
'title' => $title,
- 'individual' => null,
- 'family' => $family,
- 'name_fact' => null,
- 'famtag' => $famtag,
- 'gender' => $gender,
+ 'tree' => $tree,
+ 'url' => $request->getQueryParams()['url'] ?? $family->url(),
]);
}
}
diff --git a/app/Http/RequestHandlers/AddSpouseToIndividualAction.php b/app/Http/RequestHandlers/AddSpouseToIndividualAction.php
index a960c5384c..1049b14197 100644
--- a/app/Http/RequestHandlers/AddSpouseToIndividualAction.php
+++ b/app/Http/RequestHandlers/AddSpouseToIndividualAction.php
@@ -28,7 +28,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
-use function preg_match_all;
+use function is_string;
use function redirect;
/**
@@ -59,63 +59,33 @@ class AddSpouseToIndividualAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
-
- $individual = Registry::individualFactory()->make($xref, $tree);
- $individual = Auth::checkIndividualAccess($individual, true);
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$params = (array) $request->getParsedBody();
- $sex = $params['SEX'];
-
- $this->gedcom_edit_service->glevels = $params['glevels'] ?? [];
- $this->gedcom_edit_service->tag = $params['tag'] ?? [];
- $this->gedcom_edit_service->text = $params['text'] ?? [];
- $this->gedcom_edit_service->islink = $params['islink'] ?? [];
-
- $this->gedcom_edit_service->splitSource();
- $indi_gedcom = '0 @@ INDI';
- $indi_gedcom .= $this->gedcom_edit_service->addNewName($request, $tree);
- $indi_gedcom .= $this->gedcom_edit_service->addNewSex($request);
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $indi_gedcom .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
- if ($params['SOUR_INDI'] ?? false) {
- $indi_gedcom = $this->gedcom_edit_service->handleUpdates($indi_gedcom);
- } else {
- $indi_gedcom = $this->gedcom_edit_service->updateRest($indi_gedcom);
- }
+ $individual = Registry::individualFactory()->make($xref, $tree);
+ $individual = Auth::checkIndividualAccess($individual, true);
- $fam_gedcom = '';
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $fam_gedcom .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
- if ($params['SOUR_FAM'] ?? false) {
- $fam_gedcom = $this->gedcom_edit_service->handleUpdates($fam_gedcom);
- } else {
- $fam_gedcom = $this->gedcom_edit_service->updateRest($fam_gedcom);
- }
+ $levels = $params['ilevels'] ?? [];
+ $tags = $params['itags'] ?? [];
+ $values = $params['ivalues'] ?? [];
// Create the new spouse
- $spouse = $tree->createIndividual($indi_gedcom);
+ $gedcom = $this->gedcom_edit_service->editLinesToGedcom('INDI', $levels, $tags, $values);
+ $spouse = $tree->createIndividual("0 @@ INDI\n" . $gedcom);
+
// Create a new family
- if ($sex === 'F') {
- $family = $tree->createFamily("0 @@ FAM\n1 WIFE @" . $spouse->xref() . "@\n1 HUSB @" . $individual->xref() . '@' . $fam_gedcom);
- } else {
- $family = $tree->createFamily("0 @@ FAM\n1 HUSB @" . $spouse->xref() . "@\n1 WIFE @" . $individual->xref() . '@' . $fam_gedcom);
- }
- // Link the spouses to the family
- $spouse->createFact('1 FAMS @' . $family->xref() . '@', true);
- $individual->createFact('1 FAMS @' . $family->xref() . '@', true);
+ $i_link = "\n1 " . ($individual->sex() === 'F' ? 'WIFE' : 'HUSB') . ' @' . $individual->xref() . '@';
+ $s_link = "\n1 " . ($individual->sex() !== 'F' ? 'WIFE' : 'HUSB') . ' @' . $spouse->xref() . '@';
+ $family = $tree->createFamily("0 @@ FAM\n" . $i_link . $s_link);
+
+ // Link the individual to the family
+ $individual->createFact('1 FAMS @' . $family->xref() . '@', false);
- if (($params['goto'] ?? '') === 'new') {
- return redirect($spouse->url());
- }
+ // Link the spouse to the family
+ $spouse->createFact('1 FAMS @' . $family->xref() . '@', false);
- return redirect($individual->url());
+ return redirect($params['url'] ?? $spouse->url());
}
}
diff --git a/app/Http/RequestHandlers/AddSpouseToIndividualPage.php b/app/Http/RequestHandlers/AddSpouseToIndividualPage.php
index 3ad2f39938..09ecedfa6a 100644
--- a/app/Http/RequestHandlers/AddSpouseToIndividualPage.php
+++ b/app/Http/RequestHandlers/AddSpouseToIndividualPage.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
@@ -29,6 +30,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
+use function route;
/**
* Add a new spouse to an individual, creating a new family.
@@ -37,6 +40,12 @@ class AddSpouseToIndividualPage implements RequestHandlerInterface
{
use ViewResponseTrait;
+ // Create mixed-sex couples by default
+ private const OPPOSITE_SEX = [
+ 'F' => 'M',
+ 'M' => 'F',
+ ];
+
/**
* @param ServerRequestInterface $request
*
@@ -47,30 +56,44 @@ class AddSpouseToIndividualPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
- if ($individual->sex() === 'F') {
- $title = $individual->fullName() . ' - ' . I18N::translate('Add a husband');
- $famtag = 'HUSB';
- $gender = 'M';
- } else {
- $title = $individual->fullName() . ' - ' . I18N::translate('Add a wife');
- $famtag = 'WIFE';
- $gender = 'F';
- }
+ // Create a dummy individual, so that we can create new/empty facts.
+ $sex = self::OPPOSITE_SEX[$individual->sex()] ?? 'U';
+ $element = Registry::elementFactory()->make('INDI:NAME');
+ $dummyi = Registry::individualFactory()->new('', '0 @@ INDI', null, $tree);
+ $dummyf = Registry::familyFactory()->new('', '0 @@ FAM', null, $tree);
+ $facts = [
+ 'i' => [
+ new Fact('1 SEX ' . $sex, $dummyi, ''),
+ new Fact('1 NAME ' . $element->default($tree), $dummyi, ''),
+ new Fact('1 BIRT', $dummyi, ''),
+ new Fact('1 DEAT', $dummyi, ''),
+ ],
+ 'f' => [
+ new Fact('1 MARR', $dummyf, ''),
+ ],
+ ];
+
+ $titles = [
+ 'F' => I18N::translate('Add a wife'),
+ 'H' => I18N::translate('Add a husband'),
+ 'U' => I18N::translate('Add a spouse'),
+ ];
+
+ $title = $titles[$sex] ?? $titles['U'];
return $this->viewResponse('edit/new-individual', [
- 'next_action' => AddSpouseToIndividualAction::class,
- 'tree' => $tree,
- 'title' => $title,
- 'individual' => $individual,
- 'family' => null,
- 'name_fact' => null,
- 'famtag' => $famtag,
- 'gender' => $gender,
+ 'cancel_url' => $individual->url(),
+ 'facts' => $facts,
+ 'post_url' => route(AddSpouseToIndividualAction::class, ['tree' => $tree->name(), 'xref' => $xref]),
+ 'title' => $individual->fullName() . ' - ' . $title,
+ 'tree' => $tree,
+ 'url' => $request->getQueryParams()['url'] ?? $individual->url(),
]);
}
}
diff --git a/app/Http/RequestHandlers/AddUnlinkedAction.php b/app/Http/RequestHandlers/AddUnlinkedAction.php
index 42838c5d38..58b16c7fe6 100644
--- a/app/Http/RequestHandlers/AddUnlinkedAction.php
+++ b/app/Http/RequestHandlers/AddUnlinkedAction.php
@@ -26,9 +26,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
-use function preg_match_all;
use function redirect;
-use function route;
/**
* Create a new unlinked individual.
@@ -60,32 +58,14 @@ class AddUnlinkedAction implements RequestHandlerInterface
$params = (array) $request->getParsedBody();
- $this->gedcom_edit_service->glevels = $params['glevels'] ?? [];
- $this->gedcom_edit_service->tag = $params['tag'] ?? [];
- $this->gedcom_edit_service->text = $params['text'] ?? [];
- $this->gedcom_edit_service->islink = $params['islink'] ?? [];
+ $levels = $params['ilevels'] ?? [];
+ $tags = $params['itags'] ?? [];
+ $values = $params['ivalues'] ?? [];
- $this->gedcom_edit_service->splitSource();
- $gedrec = '0 @@ INDI';
- $gedrec .= $this->gedcom_edit_service->addNewName($request, $tree);
- $gedrec .= $this->gedcom_edit_service->addNewSex($request);
- if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
- foreach ($matches[1] as $match) {
- $gedrec .= $this->gedcom_edit_service->addNewFact($request, $tree, $match);
- }
- }
- if ($params['SOUR_INDI'] ?? false) {
- $gedrec = $this->gedcom_edit_service->handleUpdates($gedrec);
- } else {
- $gedrec = $this->gedcom_edit_service->updateRest($gedrec);
- }
+ $gedcom = $this->gedcom_edit_service->editLinesToGedcom('INDI', $levels, $tags, $values);
- $new_indi = $tree->createIndividual($gedrec);
+ $individual = $tree->createIndividual("0 @@ INDI\n" . $gedcom);
- if (($params['goto'] ?? '') === 'new') {
- return redirect($new_indi->url());
- }
-
- return redirect(route(ManageTrees::class, ['tree' => $tree->name()]));
+ return redirect($params['url'] ?? $individual->url());
}
}
diff --git a/app/Http/RequestHandlers/AddUnlinkedPage.php b/app/Http/RequestHandlers/AddUnlinkedPage.php
index 074ee70a7a..9557aa3095 100644
--- a/app/Http/RequestHandlers/AddUnlinkedPage.php
+++ b/app/Http/RequestHandlers/AddUnlinkedPage.php
@@ -19,14 +19,17 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Tree;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function route;
/**
* Create a new unlinked individual.
@@ -45,15 +48,25 @@ class AddUnlinkedPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
+ // Create a dummy individual, so that we can create new/empty facts.
+ $element = Registry::elementFactory()->make('INDI:NAME');
+ $dummy = Registry::individualFactory()->new('', '0 @@ INDI', null, $tree);
+ $facts = [
+ 'i' => [
+ new Fact('1 SEX', $dummy, ''),
+ new Fact('1 NAME ' . $element->default($tree), $dummy, ''),
+ new Fact('1 BIRT', $dummy, ''),
+ new Fact('1 DEAT', $dummy, ''),
+ ],
+ ];
+
return $this->viewResponse('edit/new-individual', [
- 'next_action' => AddUnlinkedAction::class,
- 'tree' => $tree,
- 'title' => I18N::translate('Create an individual'),
- 'individual' => null,
- 'family' => null,
- 'name_fact' => null,
- 'famtag' => '',
- 'gender' => 'U',
+ 'cancel_url' => route('manage-trees', ['tree' => $tree->name()]),
+ 'facts' => $facts,
+ 'post_url' => route(AddUnlinkedAction::class, ['tree' => $tree->name()]),
+ 'tree' => $tree,
+ 'title' => I18N::translate('Create an individual'),
+ 'url' => $request->getQueryParams()['url'] ?? route('manage-trees', ['tree' => $tree->name()]),
]);
}
}
diff --git a/app/Http/RequestHandlers/EditFactAction.php b/app/Http/RequestHandlers/EditFactAction.php
index c5e4a33c8a..9cae393376 100644
--- a/app/Http/RequestHandlers/EditFactAction.php
+++ b/app/Http/RequestHandlers/EditFactAction.php
@@ -30,15 +30,10 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
-use function array_merge;
-use function array_unique;
use function assert;
use function explode;
-use function in_array;
use function is_string;
-use function preg_match_all;
use function redirect;
-use function trim;
/**
* Save an updated GEDCOM fact.
@@ -84,78 +79,38 @@ class EditFactAction implements RequestHandlerInterface
$params = (array) $request->getParsedBody();
$keep_chan = (bool) ($params['keep_chan'] ?? false);
+ $levels = $params['levels'];
+ $tags = $params['tags'];
+ $values = $params['values'];
- $this->gedcom_edit_service->glevels = $params['glevels'];
- $this->gedcom_edit_service->tag = $params['tag'];
- $this->gedcom_edit_service->text = $params['text'];
- $this->gedcom_edit_service->islink = $params['islink'];
+ $gedcom = $this->gedcom_edit_service->editLinesToGedcom($record::RECORD_TYPE, $levels, $tags, $values);
- // If the fact has a DATE or PLAC, then delete any value of Y
- if ($this->gedcom_edit_service->text[0] === 'Y') {
- foreach ($this->gedcom_edit_service->tag as $n => $value) {
- if ($this->gedcom_edit_service->glevels[$n] == 2 && ($value === 'DATE' || $value === 'PLAC') && $this->gedcom_edit_service->text[$n] !== '') {
- $this->gedcom_edit_service->text[0] = '';
- break;
- }
- }
- }
-
- $newged = '';
-
- $NAME = $params['NAME'] ?? '';
-
- if ($NAME !== '') {
- $newged .= "\n1 NAME " . $NAME;
- $name_facts = [
- 'TYPE',
- 'NPFX',
- 'GIVN',
- 'NICK',
- 'SPFX',
- 'SURN',
- 'NSFX',
- ];
- foreach ($name_facts as $name_fact) {
- $NAME_FACT = $params[$name_fact] ?? '';
- if ($NAME_FACT !== '') {
- $newged .= "\n2 " . $name_fact . ' ' . $NAME_FACT;
- }
- }
- }
-
- $newged = $this->gedcom_edit_service->handleUpdates($newged);
+ $census_assistant = $this->module_service->findByInterface(CensusAssistantModule::class)->first();
- // Add new names after existing names
- if ($NAME !== '') {
- preg_match_all('/[_0-9A-Z]+/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match);
- $name_facts = array_unique(array_merge(['_MARNM'], $match[0]));
- foreach ($name_facts as $name_fact) {
- $NAME_FACT = $params[$name_fact] ?? '';
- // Ignore advanced facts that duplicate standard facts.
- if ($NAME_FACT !== '' && !in_array($name_fact, ['TYPE', 'NPFX', 'GIVN', 'NICK', 'SPFX', 'SURN', 'NSFX'], true)) {
- $newged .= "\n2 " . $name_fact . ' ' . $NAME_FACT;
+ if ($census_assistant instanceof CensusAssistantModule && $record instanceof Individual) {
+ $gedcom = $census_assistant->updateCensusAssistant($request, $record, $fact_id, $gedcom, $keep_chan);
+ $pid_array = $params['pid_array'] ?? '';
+ if ($pid_array !== '') {
+ foreach (explode(',', $pid_array) as $pid) {
+ if ($pid !== $xref) {
+ $individual = Registry::individualFactory()->make($pid, $tree);
+ if ($individual instanceof Individual && $individual->canEdit()) {
+ $individual->updateFact('', $gedcom, !$keep_chan);
+ }
+ }
}
}
}
- $newged = trim($newged); // Remove leading newline
-
- $census_assistant = $this->module_service->findByInterface(CensusAssistantModule::class)->first();
- if ($census_assistant instanceof CensusAssistantModule && $record instanceof Individual) {
- $newged = $census_assistant->updateCensusAssistant($request, $record, $fact_id, $newged, $keep_chan);
- }
-
- $record->updateFact($fact_id, $newged, !$keep_chan);
-
- // For the GEDFact_assistant module
- $pid_array = $params['pid_array'] ?? '';
- if ($pid_array !== '') {
- foreach (explode(',', $pid_array) as $pid) {
- if ($pid !== $xref) {
- $indi = Registry::individualFactory()->make($pid, $tree);
- if ($indi && $indi->canEdit()) {
- $indi->updateFact($fact_id, $newged, !$keep_chan);
- }
+ if ($fact_id === 'new') {
+ // Add a new fact
+ $record->updateFact('', $gedcom, !$keep_chan);
+ } else {
+ // Update (only the first copy of) an existing fact
+ foreach ($record->facts([], false, null, true) as $fact) {
+ if ($fact->id() === $fact_id && $fact->canEdit()) {
+ $record->updateFact($fact_id, $gedcom, !$keep_chan);
+ break;
}
}
}
diff --git a/app/Http/RequestHandlers/EditName.php b/app/Http/RequestHandlers/EditName.php
deleted file mode 100644
index 0c4072561d..0000000000
--- a/app/Http/RequestHandlers/EditName.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-/**
- * webtrees: online genealogy
- * Copyright (C) 2021 webtrees development team
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-declare(strict_types=1);
-
-namespace Fisharebest\Webtrees\Http\RequestHandlers;
-
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Exceptions\HttpNotFoundException;
-use Fisharebest\Webtrees\Fact;
-use Fisharebest\Webtrees\Http\ViewResponseTrait;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Registry;
-use Fisharebest\Webtrees\Tree;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Server\RequestHandlerInterface;
-
-use function assert;
-use function is_string;
-
-/**
- * Edit an individual name.
- */
-class EditName implements RequestHandlerInterface
-{
- use ViewResponseTrait;
-
- /**
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function handle(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree);
-
- $xref = $request->getAttribute('xref');
- assert(is_string($xref));
-
- $fact_id = $request->getAttribute('fact_id') ?? '';
-
- $individual = Registry::individualFactory()->make($xref, $tree);
- $individual = Auth::checkIndividualAccess($individual, true);
-
- // Find the fact to edit
- $fact = $individual->facts()
- ->first(static function (Fact $fact) use ($fact_id): bool {
- return $fact->id() === $fact_id && $fact->canEdit();
- });
-
- if ($fact instanceof Fact) {
- return $this->viewResponse('edit/new-individual', [
- 'next_action' => EditFactAction::class,
- 'tree' => $tree,
- 'title' => I18N::translate('Edit the name'),
- 'individual' => $individual,
- 'family' => null,
- 'name_fact' => $fact,
- 'famtag' => '',
- 'gender' => $individual->sex(),
- ]);
- }
-
- throw new HttpNotFoundException();
- }
-}
diff --git a/app/Http/RequestHandlers/LinkChildToFamilyAction.php b/app/Http/RequestHandlers/LinkChildToFamilyAction.php
index bae2852f20..e60e39bccf 100644
--- a/app/Http/RequestHandlers/LinkChildToFamilyAction.php
+++ b/app/Http/RequestHandlers/LinkChildToFamilyAction.php
@@ -27,6 +27,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
use function redirect;
/**
@@ -44,7 +45,8 @@ class LinkChildToFamilyAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
diff --git a/app/Http/RequestHandlers/LinkChildToFamilyPage.php b/app/Http/RequestHandlers/LinkChildToFamilyPage.php
index 24de9e6314..e76e0767f4 100644
--- a/app/Http/RequestHandlers/LinkChildToFamilyPage.php
+++ b/app/Http/RequestHandlers/LinkChildToFamilyPage.php
@@ -29,6 +29,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
/**
* Link an existing individual as child in an existing family.
@@ -47,17 +48,17 @@ class LinkChildToFamilyPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
-
$individual = Auth::checkIndividualAccess($individual, true);
- $title = $individual->fullName() . ' - ' . I18N::translate('Link this individual to an existing family as a child');
+ $title = I18N::translate('Link this individual to an existing family as a child');
return $this->viewResponse('edit/link-child-to-family', [
'individual' => $individual,
- 'title' => $title,
+ 'title' => $individual->fullName() . ' - ' . $title,
'tree' => $tree,
'xref' => $xref,
]);
diff --git a/app/Http/RequestHandlers/LinkSpouseToIndividualAction.php b/app/Http/RequestHandlers/LinkSpouseToIndividualAction.php
index b961c77699..9bda3def19 100644
--- a/app/Http/RequestHandlers/LinkSpouseToIndividualAction.php
+++ b/app/Http/RequestHandlers/LinkSpouseToIndividualAction.php
@@ -28,6 +28,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
use function redirect;
/**
@@ -58,14 +59,20 @@ class LinkSpouseToIndividualAction implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
$params = (array) $request->getParsedBody();
- $spid = $params['spid'];
+ $levels = $params['flevels'] ?? [];
+ $tags = $params['ftags'] ?? [];
+ $values = $params['fvalues'] ?? [];
+
+ // Create the new family
+ $spid = $params['spid'];
$spouse = Registry::individualFactory()->make($spid, $tree);
$spouse = Auth::checkIndividualAccess($spouse, true);
@@ -75,12 +82,12 @@ class LinkSpouseToIndividualAction implements RequestHandlerInterface
$gedcom = "0 @@ FAM\n1 WIFE @" . $individual->xref() . "@\n1 HUSB @" . $spouse->xref() . '@';
}
- $gedcom .= $this->gedcom_edit_service->addNewFact($request, $tree, 'MARR');
+ $gedcom .= "\n" . $this->gedcom_edit_service->editLinesToGedcom('FAM', $levels, $tags, $values);
$family = $tree->createFamily($gedcom);
- $individual->createFact('1 FAMS @' . $family->xref() . '@', true);
- $spouse->createFact('1 FAMS @' . $family->xref() . '@', true);
+ $individual->createFact('1 FAMS @' . $family->xref() . '@', false);
+ $spouse->createFact('1 FAMS @' . $family->xref() . '@', false);
return redirect($family->url());
}
diff --git a/app/Http/RequestHandlers/LinkSpouseToIndividualPage.php b/app/Http/RequestHandlers/LinkSpouseToIndividualPage.php
index 21a12b160e..12c7d8a12a 100644
--- a/app/Http/RequestHandlers/LinkSpouseToIndividualPage.php
+++ b/app/Http/RequestHandlers/LinkSpouseToIndividualPage.php
@@ -20,6 +20,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
@@ -29,6 +30,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function is_string;
/**
* Link an existing individual as a new spouse.
@@ -47,11 +49,20 @@ class LinkSpouseToIndividualPage implements RequestHandlerInterface
$tree = $request->getAttribute('tree');
assert($tree instanceof Tree);
- $xref = $request->getQueryParams()['xref'];
+ $xref = $request->getAttribute('xref');
+ assert(is_string($xref));
$individual = Registry::individualFactory()->make($xref, $tree);
$individual = Auth::checkIndividualAccess($individual, true);
+ // Create a dummy family record, so we can create new/empty facts.
+ $dummy = Registry::familyFactory()->new('', '0 @@ FAM', null, $tree);
+ $facts = [
+ 'f' => [
+ new Fact('1 MARR', $dummy, ''),
+ ],
+ ];
+
if ($individual->sex() === 'F') {
$title = $individual->fullName() . ' - ' . I18N::translate('Add a husband using an existing individual');
$label = I18N::translate('Husband');
@@ -61,9 +72,11 @@ class LinkSpouseToIndividualPage implements RequestHandlerInterface
}
return $this->viewResponse('edit/link-spouse-to-individual', [
- 'individual' => $individual,
+ 'post_url' => route(LinkSpouseToIndividualAction::class, ['tree' => $tree->name(), 'xref' => $xref]),
+ 'cancel_url' => $individual->url(),
'label' => $label,
'title' => $title,
+ 'facts' => $facts,
'tree' => $tree,
'xref' => $xref,
]);
diff --git a/app/Http/RequestHandlers/ModulesCustomTagsPage.php b/app/Http/RequestHandlers/ModulesCustomTagsPage.php
index 051c3a506e..6a0f28444e 100644
--- a/app/Http/RequestHandlers/ModulesCustomTagsPage.php
+++ b/app/Http/RequestHandlers/ModulesCustomTagsPage.php
@@ -40,7 +40,7 @@ class ModulesCustomTagsPage extends AbstractModuleComponentPage
{
return $this->listComponents(
ModuleCustomTagsInterface::class,
- view('icons/chart') . I18N::translate('Custom tags'),
+ view('icons/chart') . ' ' . I18N::translate('Custom GEDCOM tags'),
''
);
}
diff --git a/app/Http/Routes/WebRoutes.php b/app/Http/Routes/WebRoutes.php
index a13dfe5a27..18f74acaef 100644
--- a/app/Http/Routes/WebRoutes.php
+++ b/app/Http/Routes/WebRoutes.php
@@ -35,7 +35,6 @@ use Fisharebest\Webtrees\Http\RequestHandlers\AddChildToIndividualAction;
use Fisharebest\Webtrees\Http\RequestHandlers\AddChildToIndividualPage;
use Fisharebest\Webtrees\Http\RequestHandlers\AddMediaFileAction;
use Fisharebest\Webtrees\Http\RequestHandlers\AddMediaFileModal;
-use Fisharebest\Webtrees\Http\RequestHandlers\AddName;
use Fisharebest\Webtrees\Http\RequestHandlers\AddNewFact;
use Fisharebest\Webtrees\Http\RequestHandlers\AddParentToIndividualAction;
use Fisharebest\Webtrees\Http\RequestHandlers\AddParentToIndividualPage;
@@ -98,7 +97,6 @@ use Fisharebest\Webtrees\Http\RequestHandlers\EditFactAction;
use Fisharebest\Webtrees\Http\RequestHandlers\EditFactPage;
use Fisharebest\Webtrees\Http\RequestHandlers\EditMediaFileAction;
use Fisharebest\Webtrees\Http\RequestHandlers\EditMediaFileModal;
-use Fisharebest\Webtrees\Http\RequestHandlers\EditName;
use Fisharebest\Webtrees\Http\RequestHandlers\EditNoteAction;
use Fisharebest\Webtrees\Http\RequestHandlers\EditNotePage;
use Fisharebest\Webtrees\Http\RequestHandlers\EditRawFactAction;
@@ -528,15 +526,14 @@ class WebRoutes
$router->get(AutoCompleteFolder::class, '/autocomplete/folder/{query}');
$router->get(AutoCompletePlace::class, '/autocomplete/place/{query}');
$router->get(AutoCompleteSurname::class, '/autocomplete/surname/{query}');
- $router->get(AddChildToFamilyPage::class, '/add-child-to-family');
- $router->post(AddChildToFamilyAction::class, '/add-child-to-family');
+ $router->get(AddChildToFamilyPage::class, '/add-child-to-family/{xref}/{sex}');
+ $router->post(AddChildToFamilyAction::class, '/add-child-to-family/{xref}');
$router->get(AddNewFact::class, '/add-fact/{xref}/{fact}');
$router->post(SelectNewFact::class, '/add-fact/{xref}');
$router->get(AddMediaFileModal::class, '/add-media-file/{xref}');
$router->post(AddMediaFileAction::class, '/add-media-file/{xref}');
- $router->get(AddName::class, '/add-name');
- $router->get(AddSpouseToFamilyPage::class, '/add-spouse-to-family');
- $router->post(AddSpouseToFamilyAction::class, '/add-spouse-to-family');
+ $router->get(AddSpouseToFamilyPage::class, '/add-spouse-to-family/{xref}/{sex}');
+ $router->post(AddSpouseToFamilyAction::class, '/add-spouse-to-family/{xref}');
$router->get(ChangeFamilyMembersPage::class, '/change-family-members');
$router->post(ChangeFamilyMembersAction::class, '/change-family-members');
$router->get(CreateLocationModal::class, '/create-location');
@@ -584,19 +581,18 @@ class WebRoutes
$router->post(ReorderFamiliesAction::class, '/reorder-spouses/{xref}');
$router->get(SearchReplacePage::class, '/search-replace');
$router->post(SearchReplaceAction::class, '/search-replace');
- $router->get(AddChildToIndividualPage::class, '/add-child-to-individual');
- $router->post(AddChildToIndividualAction::class, '/add-child-to-individual');
- $router->get(AddParentToIndividualPage::class, '/add-parent-to-individual');
- $router->post(AddParentToIndividualAction::class, '/add-parent-to-individual');
- $router->get(AddSpouseToIndividualPage::class, '/add-spouse-to-individual');
- $router->post(AddSpouseToIndividualAction::class, '/add-spouse-to-individual');
+ $router->get(AddChildToIndividualPage::class, '/add-child-to-individual/{xref}');
+ $router->post(AddChildToIndividualAction::class, '/add-child-to-individual/{xref}');
+ $router->get(AddParentToIndividualPage::class, '/add-parent-to-individual/{xref}/{sex}');
+ $router->post(AddParentToIndividualAction::class, '/add-parent-to-individual/{xref}');
+ $router->get(AddSpouseToIndividualPage::class, '/add-spouse-to-individual/{xref}');
+ $router->post(AddSpouseToIndividualAction::class, '/add-spouse-to-individual/{xref}');
$router->get(AddUnlinkedPage::class, '/add-unlinked-individual');
$router->post(AddUnlinkedAction::class, '/add-unlinked-individual');
- $router->get(LinkChildToFamilyPage::class, '/link-child-to-family');
- $router->post(LinkChildToFamilyAction::class, '/link-child-to-family');
- $router->get(LinkSpouseToIndividualPage::class, '/link-spouse-to-individual');
- $router->post(LinkSpouseToIndividualAction::class, '/link-spouse-to-individual');
- $router->get(EditName::class, '/edit-name/{xref}/{fact_id}');
+ $router->get(LinkChildToFamilyPage::class, '/link-child-to-family/{xref}');
+ $router->post(LinkChildToFamilyAction::class, '/link-child-to-family/{xref}');
+ $router->get(LinkSpouseToIndividualPage::class, '/link-spouse-to-individual/{xref}');
+ $router->post(LinkSpouseToIndividualAction::class, '/link-spouse-to-individual/{xref}');
});
// User routes with a tree.
diff --git a/app/Module/CustomTagsAldfaer.php b/app/Module/CustomTagsAldfaer.php
index 37eafcbcbb..e26039e549 100644
--- a/app/Module/CustomTagsAldfaer.php
+++ b/app/Module/CustomTagsAldfaer.php
@@ -32,6 +32,16 @@ class CustomTagsAldfaer extends AbstractModule implements ModuleConfigInterface,
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsAncestry.php b/app/Module/CustomTagsAncestry.php
index 9d0430cc42..3223970efa 100644
--- a/app/Module/CustomTagsAncestry.php
+++ b/app/Module/CustomTagsAncestry.php
@@ -33,6 +33,16 @@ class CustomTagsAncestry extends AbstractModule implements ModuleConfigInterface
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsBrothersKeeper.php b/app/Module/CustomTagsBrothersKeeper.php
index 2aecdb943b..8830959b8b 100644
--- a/app/Module/CustomTagsBrothersKeeper.php
+++ b/app/Module/CustomTagsBrothersKeeper.php
@@ -30,7 +30,7 @@ use Fisharebest\Webtrees\Elements\PlaceName;
use Fisharebest\Webtrees\I18N;
/**
- * Custom tags created by Brother’s Keeper
+ * Custom GEDCOM tags created by Brother’s Keeper
*
* Class CustomTagsBrothersKeeper
*/
@@ -40,6 +40,16 @@ class CustomTagsBrothersKeeper extends AbstractModule implements ModuleConfigInt
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*
* @see http://wiki-de.genealogy.net/GEDCOM/_Nutzerdef-Tag
diff --git a/app/Module/CustomTagsFamilySearch.php b/app/Module/CustomTagsFamilySearch.php
index 92a956054b..5e10a4b469 100644
--- a/app/Module/CustomTagsFamilySearch.php
+++ b/app/Module/CustomTagsFamilySearch.php
@@ -32,6 +32,16 @@ class CustomTagsFamilySearch extends AbstractModule implements ModuleConfigInter
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsFamilyTreeBuilder.php b/app/Module/CustomTagsFamilyTreeBuilder.php
index 9264ad9a0f..99e88d8441 100644
--- a/app/Module/CustomTagsFamilyTreeBuilder.php
+++ b/app/Module/CustomTagsFamilyTreeBuilder.php
@@ -35,6 +35,16 @@ class CustomTagsFamilyTreeBuilder extends AbstractModule implements ModuleConfig
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsFamilyTreeMaker.php b/app/Module/CustomTagsFamilyTreeMaker.php
index eb19e22fd1..e779984d22 100644
--- a/app/Module/CustomTagsFamilyTreeMaker.php
+++ b/app/Module/CustomTagsFamilyTreeMaker.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Elements\NamePersonal;
use Fisharebest\Webtrees\I18N;
/**
- * Custom tags created by FamilyTreeMaker (DOS, Windows, ancestry.com)
+ * Custom GEDCOM tags created by FamilyTreeMaker (DOS, Windows, ancestry.com)
*
* Class CustomTagsFamilyTreeMaker
*/
@@ -35,6 +35,16 @@ class CustomTagsFamilyTreeMaker extends AbstractModule implements ModuleConfigIn
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*
* @see http://wiki-de.genealogy.net/GEDCOM/_Nutzerdef-Tag
diff --git a/app/Module/CustomTagsGedcom53.php b/app/Module/CustomTagsGedcom53.php
index 716f3b3536..12971ed95e 100644
--- a/app/Module/CustomTagsGedcom53.php
+++ b/app/Module/CustomTagsGedcom53.php
@@ -39,6 +39,16 @@ class CustomTagsGedcom53 extends AbstractModule implements ModuleConfigInterface
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsGedcom55.php b/app/Module/CustomTagsGedcom55.php
index 7e671e0fcf..30ce147288 100644
--- a/app/Module/CustomTagsGedcom55.php
+++ b/app/Module/CustomTagsGedcom55.php
@@ -32,6 +32,16 @@ class CustomTagsGedcom55 extends AbstractModule implements ModuleConfigInterface
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsGedcomL.php b/app/Module/CustomTagsGedcomL.php
index eee0bdcb16..cc2d8c0aea 100644
--- a/app/Module/CustomTagsGedcomL.php
+++ b/app/Module/CustomTagsGedcomL.php
@@ -74,6 +74,16 @@ class CustomTagsGedcomL extends AbstractModule implements ModuleConfigInterface,
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsLegacy.php b/app/Module/CustomTagsLegacy.php
index b657d38291..57f4e3632b 100644
--- a/app/Module/CustomTagsLegacy.php
+++ b/app/Module/CustomTagsLegacy.php
@@ -31,6 +31,16 @@ class CustomTagsLegacy extends AbstractModule implements ModuleConfigInterface,
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @see http://support.legacyfamilytree.com/article/AA-00520/0/GEDCOM-Files-custom-tags-in-Legacy.html
*
* @return array<string,ElementInterface>
diff --git a/app/Module/CustomTagsPersonalAncestralFile.php b/app/Module/CustomTagsPersonalAncestralFile.php
index 28827c3f39..1519aa5c7b 100644
--- a/app/Module/CustomTagsPersonalAncestralFile.php
+++ b/app/Module/CustomTagsPersonalAncestralFile.php
@@ -34,6 +34,16 @@ class CustomTagsPersonalAncestralFile extends AbstractModule implements ModuleCo
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*
* @see http://wiki-de.genealogy.net/GEDCOM/_Nutzerdef-Tag
diff --git a/app/Module/CustomTagsPhpGedView.php b/app/Module/CustomTagsPhpGedView.php
index e71f38ae3f..a4a679629d 100644
--- a/app/Module/CustomTagsPhpGedView.php
+++ b/app/Module/CustomTagsPhpGedView.php
@@ -44,6 +44,16 @@ class CustomTagsPhpGedView extends AbstractModule implements ModuleConfigInterfa
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsReunion.php b/app/Module/CustomTagsReunion.php
index 355ec70693..b2ada4aeb5 100644
--- a/app/Module/CustomTagsReunion.php
+++ b/app/Module/CustomTagsReunion.php
@@ -33,6 +33,16 @@ class CustomTagsReunion extends AbstractModule implements ModuleConfigInterface,
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsRootsMagic.php b/app/Module/CustomTagsRootsMagic.php
index cbc96d0219..c35cf4b9a0 100644
--- a/app/Module/CustomTagsRootsMagic.php
+++ b/app/Module/CustomTagsRootsMagic.php
@@ -33,6 +33,16 @@ class CustomTagsRootsMagic extends AbstractModule implements ModuleConfigInterfa
use ModuleCustomTagsTrait;
/**
+ * Should this module be enabled when it is first installed?
+ *
+ * @return bool
+ */
+ public function isEnabledByDefault(): bool
+ {
+ return false;
+ }
+
+ /**
* @return array<string,ElementInterface>
*/
public function customTags(): array
diff --git a/app/Module/CustomTagsWebtrees.php b/app/Module/CustomTagsWebtrees.php
index 7c98982690..9436edadd1 100644
--- a/app/Module/CustomTagsWebtrees.php
+++ b/app/Module/CustomTagsWebtrees.php
@@ -96,19 +96,27 @@ class CustomTagsWebtrees extends AbstractModule implements ModuleConfigInterface
}
/**
- * @return array<array<string>>
+ * @return array<string,array<string>>
*/
-
public function customSubTags(): array
{
return [
- ['FAM:CHAN', '_WT_USER', '0:1', ''],
- ['INDI:CHAN', '_WT_USER', '0:1', ''],
- ['NOTE:CHAN', '_WT_USER', '0:1', ''],
- ['OBJE:CHAN', '_WT_USER', '0:1', ''],
- ['REPO:CHAN', '_WT_USER', '0:1', ''],
- ['SOUR:CHAN', '_WT_USER', '0:1', ''],
- ['SUBM:CHAN', '_WT_USER', '0:1', ''],
+ 'FAM:CHAN' => [['_WT_USER']],
+ 'FAM:MARR' => [['_ASSO', '0:M', 'NOTE']],
+ 'FAM:SOUR:DATA' => [['TEXT']],
+ 'INDI:BIRT' => [['FAMC']],
+ 'INDI:CHAN' => [['_WT_USER']],
+ 'INDI:SOUR:DATA' => [['TEXT']],
+ 'NOTE' => [['RESN', '0:1', 'CHAN']],
+ 'NOTE:CHAN' => [['_WT_USER']],
+ 'OBJE' => [['RESN', '0:1', 'CHAN']],
+ 'OBJE:CHAN' => [['_WT_USER']],
+ 'REPO' => [['RESN', '0:1', 'CHAN']],
+ 'REPO:CHAN' => [['_WT_USER']],
+ 'SOUR' => [['RESN', '0:1', 'CHAN']],
+ 'SOUR:CHAN' => [['_WT_USER']],
+ 'SUBM' => [['RESN', '0:1', 'CHAN']],
+ 'SUBM:CHAN' => [['_WT_USER']],
];
}
diff --git a/app/Module/ModuleCustomTagsTrait.php b/app/Module/ModuleCustomTagsTrait.php
index 1c8b44bd97..5917f87898 100644
--- a/app/Module/ModuleCustomTagsTrait.php
+++ b/app/Module/ModuleCustomTagsTrait.php
@@ -38,36 +38,14 @@ trait ModuleCustomTagsTrait
$element_factory = Registry::elementFactory();
$element_factory->register($this->customTags());
- foreach ($this->customSubTags() as $subtags) {
- [$tag, $subtag, $repeat, $before] = $subtags;
-
- $element_factory->make($tag)->subtag($subtag, $repeat, $before);
+ foreach ($this->customSubTags() as $tag => $children) {
+ foreach ($children as $child) {
+ $element_factory->make($tag)->subtag(...$child);
+ }
}
}
/**
- * How should this module be identified in the control panel, etc.?
- *
- * @return string
- */
- public function title(): string
- {
- /* I18N: Name of a module */
- return I18N::translate('Custom tags') . ' — ' . $this->customTagApplication();
- }
-
- /**
- * A sentence describing what this module does.
- *
- * @return string
- */
- public function description(): string
- {
- /* I18N: Description of the “Custom tags” module */
- return I18N::translate('Support for non-standard GEDCOM tags.') . ' — ' . $this->customTagApplication();
- }
-
- /**
* @see https://www.gencom.org.nz/GEDCOM_tags.html
*
* @return array<string,ElementInterface>
@@ -78,7 +56,7 @@ trait ModuleCustomTagsTrait
}
/**
- * @return array<array<string>>
+ * @return array<string,array<string>>
*/
public function customSubTags(): array
{
@@ -86,6 +64,17 @@ trait ModuleCustomTagsTrait
}
/**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “Custom GEDCOM tags” module */
+ return I18N::translate('Support for non-standard GEDCOM tags.') . ' — ' . $this->customTagApplication();
+ }
+
+ /**
* The application for which we are supporting custom tags.
*
* @return string
@@ -95,7 +84,6 @@ trait ModuleCustomTagsTrait
return '';
}
-
/**
* @param ServerRequestInterface $request
*
@@ -106,8 +94,21 @@ trait ModuleCustomTagsTrait
$this->layout = 'layouts/administration';
return $this->viewResponse('modules/custom-tags/config', [
- 'tags' => $this->customTags(),
- 'title' => $this->title(),
+ 'element_factory' => Registry::elementFactory(),
+ 'subtags' => $this->customSubTags(),
+ 'tags' => $this->customTags(),
+ 'title' => $this->title(),
]);
}
+
+ /**
+ * How should this module be identified in the control panel, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module */
+ return I18N::translate('Custom GEDCOM tags') . ' — ' . $this->customTagApplication();
+ }
}
diff --git a/app/Services/GedcomService.php b/app/Services/GedcomService.php
index 32c312d204..7328f9d0e5 100644
--- a/app/Services/GedcomService.php
+++ b/app/Services/GedcomService.php
@@ -153,7 +153,7 @@ class GedcomService
'_MILITARY_SERVICE' => '_MILT',
];
- // Custom tags used by other applications, with direct synonyms
+ // Custom GEDCOM tags used by other applications, with direct synonyms
private const TAG_SYNONYMS = [
// Convert PhpGedView tag to webtrees
'_PGVU' => '_WT_USER',
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index ccbf750c97..e470b6fbc6 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1630,11 +1630,6 @@ parameters:
path: app/Services/RelationshipService.php
-
- message: "#^Method Fisharebest\\\\Webtrees\\\\Services\\\\RelationshipService\\:\\:legacyNameAlgorithm\\(\\) should return string but returns string\\|null\\.$#"
- count: 1
- path: app/Services/RelationshipService.php
-
- -
message: "#^Method Fisharebest\\\\Webtrees\\\\Services\\\\RelationshipService\\:\\:matchRelationships\\(\\) should return array\\<Fisharebest\\\\Webtrees\\\\Relationship\\> but returns array\\<array\\<string\\>\\|Fisharebest\\\\Webtrees\\\\Relationship\\>\\.$#"
count: 1
path: app/Services/RelationshipService.php
diff --git a/resources/views/admin/control-panel.phtml b/resources/views/admin/control-panel.phtml
index a36faf3b6e..9bd1da54f3 100644
--- a/resources/views/admin/control-panel.phtml
+++ b/resources/views/admin/control-panel.phtml
@@ -691,7 +691,7 @@ use Illuminate\Support\Collection;
<li>
<span class="fa-li"><?= view('icons/tag') ?></span>
<a href="<?= e(route(ModulesCustomTagsPage::class)) ?>">
- <?= I18N::translate('Custom tags') ?>
+ <?= I18N::translate('Custom GEDCOM tags') ?>
</a>
<?= view('components/badge', ['count' => $custom_tags_modules_enabled->count(), 'total' => $custom_tags_modules_disabled->count(), 'context' => 'primary']) ?>
</li>
diff --git a/resources/views/cards/add-associate.phtml b/resources/views/cards/add-associate.phtml
deleted file mode 100644
index e69e934f9e..0000000000
--- a/resources/views/cards/add-associate.phtml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var string $id
- * @var int $level
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-associate-<?= e($id) ?>" aria-expanded="false" aria-controls="add-associate">
- <?= I18N::translate('Associate') ?>
- </a>
-
- <div class="card-body collapse" id="add-associate-<?= e($id) ?>">
- <?= FunctionsEdit::addSimpleTag($tree, $level . ' _ASSO') ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' RELA') ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' NOTE') ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' SHARED_NOTE') ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-fact.phtml b/resources/views/cards/add-fact.phtml
deleted file mode 100644
index e8ae311ecc..0000000000
--- a/resources/views/cards/add-fact.phtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\GedcomTag;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var string $tag
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-fact-<?= $tag ?>" aria-expanded="false" aria-controls="add-fact-<?= $tag ?>">
- <?= GedcomTag::getLabel($tag) ?>
- </a>
-
- <div class="card-body collapse" id="add-fact-<?= $tag ?>">
- <?php FunctionsEdit::addSimpleTags($tree, $tag) ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-media-object.phtml b/resources/views/cards/add-media-object.phtml
deleted file mode 100644
index f7dbef1153..0000000000
--- a/resources/views/cards/add-media-object.phtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var int $level
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-media-object" aria-expanded="false" aria-controls="add-media-object">
- <?= I18N::translate('Add a media object') ?>
- </a>
-
- <div class="card-body collapse" id="add-media-object">
- <?= FunctionsEdit::addSimpleTag($tree, $level . ' OBJE') ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-note.phtml b/resources/views/cards/add-note.phtml
deleted file mode 100644
index 85316052d5..0000000000
--- a/resources/views/cards/add-note.phtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var int $level
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-note" aria-expanded="false" aria-controls="add-note">
- <?= I18N::translate('Note') ?>
- </a>
-
- <div class="card-body collapse" id="add-note">
- <?= FunctionsEdit::addSimpleTag($tree, $level . ' NOTE') ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-restriction.phtml b/resources/views/cards/add-restriction.phtml
deleted file mode 100644
index 263ed56c33..0000000000
--- a/resources/views/cards/add-restriction.phtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var int $level
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-restriction" aria-expanded="false" aria-controls="add-restriction">
- <?= I18N::translate('Restriction') ?>
- </a>
-
- <div class="card-body collapse" id="add-restriction">
- <?= FunctionsEdit::addSimpleTag($tree, $level . ' RESN') ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-shared-note.phtml b/resources/views/cards/add-shared-note.phtml
deleted file mode 100644
index 03cf4c4124..0000000000
--- a/resources/views/cards/add-shared-note.phtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var int $level
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-note-object" aria-expanded="false" aria-controls="add-note-object">
- <?= I18N::translate('Shared note') ?>
- </a>
-
- <div class="card-body collapse" id="add-note-object">
- <?= FunctionsEdit::addSimpleTag($tree, $level . ' SHARED_NOTE') ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-sour-data-even.phtml b/resources/views/cards/add-sour-data-even.phtml
deleted file mode 100644
index 943e2b153b..0000000000
--- a/resources/views/cards/add-sour-data-even.phtml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-
-/**
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-sour-data-even-object" aria-expanded="false" aria-controls="add-sour-data-even-object">
- <?= I18N::translate('Add an event') ?>
- </a>
-
- <div class="card-body collapse" id="add-sour-data-even-object">
- <?= FunctionsEdit::addSimpleTag($tree, '2 EVEN') ?>
- <?= FunctionsEdit::addSimpleTag($tree, '3 DATE') ?>
- <?= FunctionsEdit::addSimpleTag($tree, '3 PLAC') ?>
- </div>
-</div>
diff --git a/resources/views/cards/add-source-citation.phtml b/resources/views/cards/add-source-citation.phtml
deleted file mode 100644
index bcc3c744e7..0000000000
--- a/resources/views/cards/add-source-citation.phtml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Fact;
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\Gedcom;
-use Fisharebest\Webtrees\GedcomTag;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-use Illuminate\Support\Collection;
-
-/**
- * @var string $bdm
- * @var bool $full_citations
- * @var int $level
- * @var string $prefer_level2_sources
- * @var string $quick_required_facts
- * @var string $quick_required_famfacts
- * @var Tree $tree
- */
-
-?>
-
-<div class="card mb-4">
- <a class="card-header" href="#" data-toggle="collapse" data-target="#add-source-citation" aria-expanded="false" aria-controls="add-source-citation">
- <?= I18N::translate('Source') ?>
- </a>
-
- <div class="card-body collapse" id="add-source-citation">
- <?= FunctionsEdit::addSimpleTag($tree, $level . ' SOUR') ?>
-
- <?php if ($level === 1) : ?>
- <div class="row">
- <div class="col-sm-3"></div>
- <div class="col-sm-9">
- <?php if (str_contains($bdm, 'B')) : ?>
- <label>
- <input type="checkbox" name="SOUR_INDI" <?= $prefer_level2_sources === '2' ? 'checked' : '' ?> value="1">
- <?= I18N::translate('Individual') ?>
- </label>
- <?php if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $quick_required_facts, $matches)) : ?>
- <?php foreach (Fact::sortFactTags(new Collection($matches[1])) as $match) : ?>
- <label>
- <input type="checkbox" name="SOUR_<?= $match ?>" <?= $prefer_level2_sources === '1' ? 'checked' : '' ?> value="1">
- <?= GedcomTag::getLabel($match) ?>
- </label>
- <?php endforeach ?>
- <?php endif ?>
- <?php endif ?>
-
- <?php if (str_contains($bdm, 'M')) : ?>
- <label>
- <input type="checkbox" name="SOUR_FAM" <?= $prefer_level2_sources === '2' ? 'checked' : '' ?> value="1">
- <?= I18N::translate('Family') ?>
- </label>
- <?php if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $quick_required_famfacts, $matches)) : ?>
- <?php foreach (Fact::sortFactTags(new Collection($matches[1])) as $match) : ?>
- <label>
- <input type="checkbox" name="SOUR_<?= $match ?>" <?= $prefer_level2_sources === '1' ? 'checked' : '' ?> value="1">
- <?= GedcomTag::getLabel($match) ?>
- </label>
- <?php endforeach ?>
- <?php endif ?>
- <?php endif ?>
- </div>
- </div>
- <?php endif ?>
-
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' PAGE') ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' DATA') ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 2) . ' TEXT') ?>
-
- <?php if ($full_citations) : ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 2) . ' DATE', '', I18N::translate('Date of entry in original source')) ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' QUAY') ?>
- <?php endif ?>
-
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' OBJE') ?>
- <?= FunctionsEdit::addSimpleTag($tree, ($level + 1) . ' SHARED_NOTE') ?>
- </div>
-</div>
diff --git a/resources/views/edit/add-fact.phtml b/resources/views/edit/add-fact.phtml
deleted file mode 100644
index 6bf82e34ac..0000000000
--- a/resources/views/edit/add-fact.phtml
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Config;
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\GedcomRecord;
-use Fisharebest\Webtrees\GedcomTag;
-use Fisharebest\Webtrees\Http\RequestHandlers\EditFactAction;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Tree;
-use Ramsey\Uuid\Uuid;
-
-/**
- * @var string $fact
- * @var GedcomRecord $record
- * @var string $title
- * @var Tree $tree
- */
-
-?>
-
-<h2 class="wt-page-title"><?= $title ?></h2>
-
-<form method="post" action="<?= e(route(EditFactAction::class, ['tree' => $tree->name(), 'xref' => $record->xref()])) ?>" class="wt-page-content">
- <?= csrf_field() ?>
-
- <?php FunctionsEdit::createAddForm($tree, $fact) ?>
-
- <?php if ($record->tag() === 'SOUR' && $fact === 'DATA') : ?>
- <?= view('cards/add-note', ['level' => 2, 'tree' => $tree]) ?>
- <?= view('cards/add-shared-note', ['level' => 2, 'tree' => $tree]) ?>
- <?php endif ?>
-
- <?php if (($record->tag() === 'INDI' || $record->tag() === 'FAM') && $fact !== 'OBJE' && $fact !== 'NOTE' && $fact !== 'SHARED_NOTE' && $fact !== 'REPO' && $fact !== 'SOUR' && $fact !== 'SUBM' && $fact !== 'ASSO' && $fact !== 'ALIA' && $fact !== 'SEX') : ?>
- <?= view('cards/add-source-citation', [
- 'level' => 2,
- 'full_citations' => $tree->getPreference('FULL_SOURCES'),
- 'tree' => $tree,
- ]) ?>
-
- <?php if ($tree->getPreference('MEDIA_UPLOAD') >= Auth::accessLevel($tree)) : ?>
- <?= view('cards/add-media-object', [
- 'level' => 2,
- 'tree' => $tree,
- ]) ?>
- <?php endif ?>
-
- <?php if ($fact !== 'NOTE') : ?>
- <?= view('cards/add-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]) ?>
-
- <?= view('cards/add-shared-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]) ?>
- <?php endif ?>
-
- <?= view('cards/add-associate', [
- 'id' => Uuid::uuid4()->toString(),
- 'level' => 2,
- 'tree' => $tree,
- ]) ?>
- <?php if (in_array($fact, Config::twoAssociates(), true)) : ?>
- <?= view('cards/add-associate', [
- 'id' => Uuid::uuid4()->toString(),
- 'level' => 2,
- 'tree' => $tree,
- ]) ?>
- <?php endif ?>
-
- <?= view('cards/add-restriction', [
- 'level' => 2,
- 'tree' => $tree,
- ]) ?>
- <?php endif ?>
-
- <div class="form-group row">
- <label class="col-sm-3 col-form-label" for="keep_chan">
- <?= I18N::translate('Last change') ?>
- </label>
- <div class="col-sm-9">
- <?= view('components/checkbox-inline', ['label' => I18N::translate('Keep the existing “last change” information'), 'name' => 'keep_chan', 'checked' => (bool) $tree->getPreference('NO_UPDATE_CHAN')]) ?>
- <?= GedcomTag::getLabelValue('DATE', view('components/datetime', ['timestamp' => $record->lastChangeTimestamp()])) ?>
- <?= GedcomTag::getLabelValue('_WT_USER', e($record->lastChangeUser())) ?>
- </div>
- </div>
-
- <div class="form-group row">
- <div class="col-sm-3 wt-page-options-label">
- </div>
- <div class="col-sm-9 wt-page-options-value">
- <button class="btn btn-primary" type="submit">
- <?= view('icons/save') ?>
- <?= /* I18N: A button label. */
- I18N::translate('save') ?>
- </button>
- <a class="btn btn-secondary" href="<?= e($record->url()) ?>">
- <?= view('icons/cancel') ?>
- <?= /* I18N: A button label. */
- I18N::translate('cancel') ?>
- </a>
- </div>
- </div>
-</form>
-
-<?= view('modals/on-screen-keyboard') ?>
-<?= view('modals/ajax') ?>
-<?= view('edit/initialize-calendar-popup') ?>
diff --git a/resources/views/edit/edit-fact.phtml b/resources/views/edit/edit-fact.phtml
index fc1350343c..3af77ad995 100644
--- a/resources/views/edit/edit-fact.phtml
+++ b/resources/views/edit/edit-fact.phtml
@@ -1,14 +1,10 @@
<?php
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Config;
use Fisharebest\Webtrees\Fact;
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Http\RequestHandlers\EditFactAction;
use Fisharebest\Webtrees\Http\RequestHandlers\EditRawFactPage;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Tree;
-use Ramsey\Uuid\Uuid;
/**
* @var bool $can_edit_raw
@@ -26,84 +22,7 @@ use Ramsey\Uuid\Uuid;
<?= csrf_field() ?>
<input type="hidden" name="url" value="<?= e($url) ?>">
- <?php FunctionsEdit::createEditForm($fact) ?>
-
- <?php
- $level1type = $fact->getTag();
- switch ($fact->record()->tag()) {
- case 'SOUR':
- if ($level1type === 'DATA') {
- // SOUR:DATA facts may take a NOTE (but the SOUR record may not).
- echo view('cards/add-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- echo view('cards/add-shared-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- // SOUR:DATA facts may also take multiple EVEN.
- echo view('cards/add-sour-data-even', [
- 'tree' => $tree,
- ]);
- }
- break;
- case 'FAM':
- case 'INDI':
- // FAM and INDI records have real facts. They can take NOTE/SOUR/OBJE/etc.
- if ($level1type !== 'SEX' && $level1type !== 'NOTE' && $level1type !== 'ALIA') {
- if ($level1type !== 'SOUR') {
- echo view('cards/add-source-citation', [
- 'level' => 2,
- 'full_citations' => $tree->getPreference('FULL_SOURCES'),
- 'tree' => $tree,
- ]);
- }
- if ($level1type !== 'OBJE') {
- if ($tree->getPreference('MEDIA_UPLOAD') >= Auth::accessLevel($tree)) {
- echo view('cards/add-media-object', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- }
- }
- echo view('cards/add-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- echo view('cards/add-shared-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- if ($level1type !== 'ASSO' && $level1type !== 'NOTE' && $level1type !== 'SOUR') {
- echo view('cards/add-associate', [
- 'id' => Uuid::uuid4()->toString(),
- 'level' => 2,
- 'tree' => $tree,
- ]);
- }
- // allow to add godfather and godmother for CHR fact or best man and bridesmaid for MARR fact in one window
- if (in_array($level1type, Config::twoAssociates(), true)) {
- echo view('cards/add-associate', [
- 'id' => Uuid::uuid4()->toString(),
- 'level' => 2,
- 'tree' => $tree,
- ]);
- }
- if ($level1type !== 'SOUR') {
- echo view('cards/add-restriction', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- }
- }
- break;
- default:
- // Other types of record do not have these lower-level records
- break;
- }
-
- ?>
+ <?= view('edit/edit-gedcom-fields', ['gedcom' => $fact->insertMissingSubtags(), 'hierarchy' => explode(':', $fact->tag()), 'tree' => $fact->record()->tree(), 'prefix' => '']) ?>
<div class="form-group row">
<div class="col-sm-3 wt-page-options-label">
diff --git a/resources/views/edit/link-spouse-to-individual.phtml b/resources/views/edit/link-spouse-to-individual.phtml
index 4091153080..7e5bc01d1d 100644
--- a/resources/views/edit/link-spouse-to-individual.phtml
+++ b/resources/views/edit/link-spouse-to-individual.phtml
@@ -1,36 +1,44 @@
<?php
-use Fisharebest\Webtrees\Functions\FunctionsEdit;
-use Fisharebest\Webtrees\Http\RequestHandlers\LinkSpouseToIndividualAction;
use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Tree;
/**
- * @var Individual $individual
- * @var string $label
- * @var string $title
- * @var Tree $tree
- * @var string $xref
+ * @var string $title
+ * @var string $label
+ * @var string $cancel_url
+ * @var string $post_url
+ * @var Tree $tree
*/
?>
<h2 class="wt-page-title"><?= $title ?></h2>
-<form method="post" action="<?= e(route(LinkSpouseToIndividualAction::class, ['tree' => $tree->name(), 'xref' => $xref])) ?>" class="wt-page-content">
+<form method="post" action="<?= e($post_url) ?>" class="wt-page-content">
<?= csrf_field() ?>
- <div class="form-group row">
- <label class="col-sm-3 col-form-label" for="spouse">
+ <div class="card mb-2">
+ <div class="card-header">
<?= $label ?>
- </label>
- <div class="col-sm-9">
+ </div>
+ <div class="card-body pb-1">
<?= view('components/select-individual', ['name' => 'spid', 'id' => 'spouse', 'tree' => $tree]) ?>
</div>
</div>
- <?= FunctionsEdit::addSimpleTags($tree, 'MARR') ?>
+ <?php foreach ($facts ?? [] as $prefix => $prefix_facts) : ?>
+ <?php foreach ($prefix_facts as $fact) : ?>
+ <div class="card mb-2">
+ <div class="card-header">
+ <?= $fact->label() ?>
+ </div>
+ <div class="card-body pb-1">
+ <?= view('edit/edit-gedcom-fields', ['gedcom' => $fact->insertMissingSubtags(), 'hierarchy' => explode(':', $fact->tag()), 'tree' => $fact->record()->tree(), 'prefix' => $prefix]) ?>
+ </div>
+ </div>
+ <?php endforeach ?>
+ <?php endforeach ?>
<div class="row form-group">
<div class="col-sm-9 offset-sm-3">
@@ -39,7 +47,7 @@ use Fisharebest\Webtrees\Tree;
<?= /* I18N: A button label. */
I18N::translate('save') ?>
</button>
- <a class="btn btn-secondary" href="<?= e($individual->url()) ?>">
+ <a class="btn btn-secondary" href="<?= e($cancel_url) ?>">
<?= view('icons/cancel') ?>
<?= /* I18N: A button label. */
I18N::translate('cancel') ?>
@@ -47,5 +55,3 @@ use Fisharebest\Webtrees\Tree;
</div>
</div>
</form>
-
-<?= view('modals/ajax') ?>
diff --git a/resources/views/edit/new-individual.phtml b/resources/views/edit/new-individual.phtml
index d64e097f6f..2c92ba3bd0 100644
--- a/resources/views/edit/new-individual.phtml
+++ b/resources/views/edit/new-individual.phtml
@@ -11,344 +11,57 @@ use Fisharebest\Webtrees\Http\RequestHandlers\AddParentToIndividualAction;
use Fisharebest\Webtrees\Http\RequestHandlers\AddSpouseToFamilyAction;
use Fisharebest\Webtrees\Http\RequestHandlers\AddSpouseToIndividualAction;
use Fisharebest\Webtrees\Http\RequestHandlers\AddUnlinkedAction;
-use Fisharebest\Webtrees\Http\RequestHandlers\EditFactAction;
use Fisharebest\Webtrees\Http\RequestHandlers\EditRawFactPage;
+use Fisharebest\Webtrees\Http\RequestHandlers\EditFactAction;
use Fisharebest\Webtrees\Http\RequestHandlers\ManageTrees;
use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Individual;
-use Fisharebest\Webtrees\Registry;
-use Fisharebest\Webtrees\SurnameTradition;
use Fisharebest\Webtrees\Tree;
-use Fisharebest\Webtrees\View;
-use Illuminate\Support\Collection;
/**
- * @var Family|null $family
- * @var string $famtag
- * @var string $gender
- * @var Individual|null $individual
- * @var Fact|null $name_fact
- * @var string $next_action
- * @var string $title
- * @var Tree $tree
+ * @var string $cancel_url
+ * @var string $post_url
+ * @var string $title
+ * @var Tree $tree
+ * @var string $url
*/
?>
-<?php
-if ($individual instanceof Individual) {
- $xref = $individual->xref();
- $cancel_url = $individual->url();
-} elseif ($family !== null) {
- $xref = $family->xref();
- $cancel_url = $family->url();
-} else {
- $cancel_url = route(ManageTrees::class, ['tree' => $tree->name()]);
- $xref = 'new';
-}
-
-// Different cultures do surnames differently
-$surname_tradition = SurnameTradition::create($tree->getPreference('SURNAME_TRADITION'));
-
-if ($name_fact instanceof Fact) {
- // Editing an existing name
- $name_fact_id = $name_fact->id();
- $namerec = $name_fact->gedcom();
- $name_fields = [
- 'NAME' => $name_fact->value(),
- 'TYPE' => $name_fact->attribute('TYPE'),
- 'NPFX' => $name_fact->attribute('NPFX'),
- 'GIVN' => $name_fact->attribute('GIVN'),
- 'NICK' => $name_fact->attribute('NICK'),
- 'SPFX' => $name_fact->attribute('SPFX'),
- 'SURN' => $name_fact->attribute('SURN'),
- 'NSFX' => $name_fact->attribute('NSFX'),
- ];
-} else {
- // Creating a new name
- $name_fact_id = '';
- $namerec = '';
- $name_fields = [
- 'NAME' => '',
- 'TYPE' => '',
- 'NPFX' => '',
- 'GIVN' => '',
- 'NICK' => '',
- 'SPFX' => '',
- 'SURN' => '',
- 'NSFX' => '',
- ];
-
- // Inherit surname from parents, spouse or child
- if ($family) {
- $father = $family->husband();
- if ($father instanceof Individual && $father->facts(['NAME'])->isNotEmpty()) {
- $father_name = $father->facts(['NAME'])->first()->value();
- } else {
- $father_name = '';
- }
- $mother = $family->wife();
- if ($mother instanceof Individual && $mother->facts(['NAME'])->isNotEmpty()) {
- $mother_name = $mother->facts(['NAME'])->first()->value();
- } else {
- $mother_name = '';
- }
- } else {
- $father = null;
- $mother = null;
- $father_name = '';
- $mother_name = '';
- }
- if ($individual && $individual->facts(['NAME'])->isNotEmpty()) {
- $indi_name = $individual->facts(['NAME'])->first()->value();
- } else {
- $indi_name = '';
- }
-
- switch ($next_action) {
- case AddChildToFamilyAction::class:
- $name_fields = array_merge($name_fields, $surname_tradition->newChildNames($father_name, $mother_name, $gender));
- break;
- case AddChildToIndividualAction::class:
- if ($individual->sex() === 'F') {
- $name_fields = array_merge($name_fields, $surname_tradition->newChildNames('', $indi_name, $gender));
- } else {
- $name_fields = array_merge($name_fields, $surname_tradition->newChildNames($indi_name, '', $gender));
- }
- break;
- case AddParentToIndividualAction::class:
- $name_fields = array_merge($name_fields, $surname_tradition->newParentNames($indi_name, $gender));
- break;
- case AddSpouseToFamilyAction::class:
- if ($father) {
- $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($father_name, $gender));
- } else {
- $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($mother_name, $gender));
- }
- break;
- case AddSpouseToIndividualAction::class:
- $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($indi_name, $gender));
- break;
- case AddUnlinkedAction::class:
- case EditFactAction::class:
- if ($surname_tradition->hasSurnames()) {
- $name_fields['NAME'] = '//';
- }
- break;
- }
-}
-
-$bdm = ''; // used to copy '1 SOUR' to '2 SOUR' for BIRT DEAT MARR
-
-?>
<h2 class="wt-page-title"><?= $title ?></h2>
-<form method="post" action="<?= e(route($next_action, ['tree' => $tree->name(), 'xref' => $xref, 'fact_id' => $name_fact ? $name_fact->id() : null])) ?>" onsubmit="return checkform();">
- <input type="hidden" name="fact_id" value="<?= e($name_fact_id) ?>">
- <input type="hidden" name="famtag" value="<?= e($famtag) ?>">
- <input type="hidden" name="gender" value="<?= $gender ?>">
+<form method="post" action="<?= e($post_url) ?>">
<?= csrf_field() ?>
- <?php if ($next_action === AddChildToFamilyAction::class || $next_action === AddChildToIndividualAction::class) : ?>
- <?= FunctionsEdit::addSimpleTag($tree, '0 PEDI') ?>
- <?php endif ?>
-
- <?php
- // If we are adding a new individual, choose the sex.
- if ($next_action !== EditFactAction::class) {
- if ($famtag === 'HUSB' || $gender === 'M') {
- echo FunctionsEdit::addSimpleTag($tree, '0 SEX M');
- } elseif ($famtag === 'WIFE' || $gender === 'F') {
- echo FunctionsEdit::addSimpleTag($tree, '0 SEX F');
- } else {
- echo FunctionsEdit::addSimpleTag($tree, '0 SEX');
- }
- }
- ?>
-
- <?php
- // First - standard name fields
- foreach ($name_fields as $tag => $value) {
- if (substr_compare($tag, '_', 0, 1) !== 0) {
- echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag . ' ' . $value, '', '');
- }
- }
+ <?php foreach ($facts ?? [] as $prefix => $prefix_facts) : ?>
+ <?php foreach ($prefix_facts as $fact) : ?>
+ <div class="card mb-2">
+ <div class="card-header">
+ <?= $fact->label() ?>
+ </div>
+ <div class="card-body pb-1">
+ <?= view('edit/edit-gedcom-fields', ['gedcom' => $fact->insertMissingSubtags(), 'hierarchy' => explode(':', $fact->tag()), 'tree' => $fact->record()->tree(), 'prefix' => $prefix]) ?>
+ </div>
+ </div>
+ <?php endforeach ?>
+ <?php endforeach ?>
- // Second - advanced name fields
- if ($surname_tradition->hasMarriedNames() || preg_match('/\n2 _MARNM /', $namerec)) {
- $adv_name_fields = ['_MARNM' => ''];
- } else {
- $adv_name_fields = [];
- }
- if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match)) {
- foreach ($match[1] as $tag) {
- // Ignore advanced facts that duplicate standard facts
- if (!in_array($tag, ['TYPE', 'NPFX', 'GIVN', 'NICK', 'SPFX', 'SURN', 'NSFX'])) {
- $adv_name_fields[$tag] = '';
- }
- }
- }
-
- foreach (array_keys($adv_name_fields) as $tag) {
- // Edit existing tags, grouped together
- if (preg_match_all('/2 ' . $tag . ' (.+)/', $namerec, $match)) {
- foreach ($match[1] as $value) {
- $label = Registry::elementFactory()->make('INDI:NAME:' . $tag)->label();
- echo FunctionsEdit::addSimpleTag($tree, '2 ' . $tag . ' ' . $value, '', $label);
- if ($tag === '_MARNM') {
- preg_match_all('/\/([^\/]*)\//', $value, $matches);
- echo FunctionsEdit::addSimpleTag($tree, '2 _MARNM_SURN ' . implode(',', $matches[1]));
- }
- }
- }
- // Allow a new tag to be entered
- if (!array_key_exists($tag, $name_fields)) {
- $label = Registry::elementFactory()->make('INDI:NAME:' . $tag)->label();
- echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag, '', $label);
- if ($tag === '_MARNM') {
- echo FunctionsEdit::addSimpleTag($tree, '0 _MARNM_SURN');
- }
- }
- }
-
- // Third - new/existing custom name fields
- foreach ($name_fields as $tag => $value) {
- if (substr_compare($tag, '_', 0, 1) === 0) {
- echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag . ' ' . $value);
- if ($tag === '_MARNM') {
- preg_match_all('/\/([^\/]*)\//', $value, $matches);
- echo FunctionsEdit::addSimpleTag($tree, '2 _MARNM_SURN ' . implode(',', $matches[1]));
- }
- }
- }
-
- // Fourth - SOUR, NOTE, _CUSTOM, etc.
- if ($namerec !== '') {
- $gedlines = explode("\n", $namerec); // -- find the number of lines in the record
- $fields = explode(' ', $gedlines[0]);
- $glevel = $fields[0];
- $level = $glevel;
- $type = $fields[1];
- $tags = [];
- $i = 0;
- do {
- if ($type !== 'TYPE' && !array_key_exists($type, $name_fields) && !array_key_exists($type, $adv_name_fields)) {
- $text = '';
- for ($j = 2; $j < count($fields); $j++) {
- if ($j > 2) {
- $text .= ' ';
- }
- $text .= $fields[$j];
- }
- while (($i + 1 < count($gedlines)) && (preg_match('/' . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0)) {
- $text .= "\n" . $cmatch[1];
- $i++;
- }
- echo FunctionsEdit::addSimpleTag($tree, $level . ' ' . $type . ' ' . $text);
- }
- $tags[] = $type;
- $i++;
- if (isset($gedlines[$i])) {
- $fields = explode(' ', $gedlines[$i]);
- $level = $fields[0];
- if (isset($fields[1])) {
- $type = $fields[1];
- }
- }
- } while (($level > $glevel) && ($i < count($gedlines)));
- }
-
- // If we are adding a new individual, add the basic details
- if ($next_action !== EditFactAction::class) {
- $bdm = 'BD';
- $tags = new Collection();
- preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches);
- $tags = $tags->merge($matches[1]);
-
- // If adding a spouse add the option to add a marriage fact to the new family
- if ($next_action === AddSpouseToIndividualAction::class || $next_action === AddSpouseToFamilyAction::class) {
- $bdm .= 'M';
- preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches);
- $tags = $tags->merge($matches[1]);
- }
-
- foreach (Fact::sortFactTags($tags) as $tag) {
- echo view('cards/add-fact', [
- 'tag' => $tag,
- 'tree' => $tree,
- ]);
- }
- }
-
- if ($next_action === EditFactAction::class ) {
- // GEDCOM 5.5.1 spec says NAME doesn’t get a OBJE
- echo view('cards/add-source-citation', [
- 'level' => 2,
- 'full_citations' => $tree->getPreference('FULL_SOURCES'),
- 'tree' => $tree,
- ]);
- echo view('cards/add-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- echo view('cards/add-shared-note', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- echo view('cards/add-restriction', [
- 'level' => 2,
- 'tree' => $tree,
- ]);
- } else {
- echo view('cards/add-source-citation', [
- 'bdm' => $bdm,
- 'level' => 1,
- 'full_citations' => (bool) $tree->getPreference('FULL_SOURCES'),
- 'prefer_level2_sources' => $tree->getPreference('PREFER_LEVEL2_SOURCES'),
- 'quick_required_facts' => $tree->getPreference('QUICK_REQUIRED_FACTS'),
- 'quick_required_famfacts' => $tree->getPreference('QUICK_REQUIRED_FAMFACTS'),
- 'tree' => $tree,
- ]);
- echo view('cards/add-note', [
- 'level' => 1,
- 'tree' => $tree,
- ]);
- echo view('cards/add-shared-note', [
- 'level' => 1,
- 'tree' => $tree,
- ]);
- echo view('cards/add-restriction', [
- 'level' => 1,
- 'tree' => $tree,
- ]);
- }
-
- ?>
<div class="row form-group">
<div class="col-sm-9 offset-sm-3">
- <button class="btn btn-primary" type="submit">
+ <button class="btn btn-primary" type="submit" name="url" value="<?= e($url) ?>">
<?= view('icons/save') ?>
<?= /* I18N: A button label. */
I18N::translate('save') ?>
</button>
- <?php if ($next_action !== EditFactAction::class) : ?>
- <button class="btn btn-primary" type="submit" name="goto" value="new">
- <?= view('icons/save') ?>
- <?= /* I18N: A button label. */
- I18N::translate('go to new individual') ?>
- </button>
- <?php endif ?>
+ <button class="btn btn-primary" type="submit">
+ <?= view('icons/save') ?>
+ <?= /* I18N: A button label. */
+ I18N::translate('go to new individual') ?>
+ </button>
<a class="btn btn-secondary" href="<?= e($cancel_url) ?>">
<?= view('icons/cancel') ?>
<?= /* I18N: A button label. */
I18N::translate('cancel') ?>
</a>
-
- <?php if ($name_fact instanceof Fact && (Auth::isAdmin() || $tree->getPreference('SHOW_GEDCOM_RECORD'))) : ?>
- <a class="btn btn-link" href="<?= e(route(EditRawFactPage::class, ['xref' => $xref, 'fact_id' => $name_fact->id(), 'tree' => $tree->name()])) ?>">
- <?= I18N::translate('Edit the raw GEDCOM') ?>
- </a>
- <?php endif ?>
</div>
</div>
</form>
@@ -356,160 +69,3 @@ $bdm = ''; // used to copy '1 SOUR' to '2 SOUR' for BIRT DEAT MARR
<?= view('modals/on-screen-keyboard') ?>
<?= view('modals/ajax') ?>
<?= view('edit/initialize-calendar-popup') ?>
-
-<?php View::push('javascript') ?>
-<script>
- var SURNAME_TRADITION = <?= json_encode($tree->getPreference('SURNAME_TRADITION')) ?>;
-
- var NAME = $("[name=NAME]");
-
- // Generate a full name from the name components
- function generate_name() {
- var npfx = document.querySelector("[name=NPFX]").value;
- var givn = document.querySelector("[name=GIVN]").value;
- var spfx = document.querySelector("[name=SPFX]").value;
- var surn = document.querySelector("[name=SURN]").value;
- var nsfx = document.querySelector("[name=NSFX]").value;
- var sex_input = document.querySelector("[name=SEX]:checked");
- var sex = sex_input ? sex_input.value : "U";
-
- return webtrees.buildNameFromParts(npfx, givn, spfx, surn, nsfx, sex);
- }
-
- // Update the NAME and _MARNM fields from the name components
- // and also display the value in read-only "gedcom" format.
- function updatewholename() {
- // Don’t update the name if the user manually changed it
- if (manualChange) {
- return;
- }
-
- var npfx = document.querySelector("[name=NPFX]").value;
- var givn = document.querySelector("[name=GIVN]").value;
- var spfx = document.querySelector("[name=SPFX]").value;
- var surn = document.querySelector("[name=SURN]").value;
- var nsfx = document.querySelector("[name=NSFX]").value;
- var name = generate_name();
-
- var display_id = NAME.attr("id") + "_display";
-
- NAME.val(name);
- $("#" + display_id).text(name);
-
- // Married names inherit some NSFX values, but not these
- nsfx = nsfx.replace(/^(I|II|III|IV|V|VI|Junior|Jr\.?|Senior|Sr\.?)$/i, "");
-
- // Update _MARNM field from _MARNM_SURN field and display it
- var ip = document.getElementsByTagName("input");
- var marnm_id = "";
- var romn = "";
- var heb = "";
- var i;
-
- for (i = 0; i < ip.length; i++) {
- if (ip[i].id.indexOf("_HEB") === 0) {
- // Remember this field - we might need it later
- heb = val;
- }
- if (ip[i].id.indexOf("ROMN") === 0) {
- // Remember this field - we might need it later
- romn = val;
- }
- }
-
- for (i = 0; i < ip.length; i++) {
- var val = ip[i].value;
-
- if (ip[i].id.indexOf("_MARNM") === 0) {
- if (ip[i].id.indexOf("_MARNM_SURN") === 0) {
- var msurn = "";
- if (val !== "") {
- if (surn === "" || webtrees.detectScript(val) === webtrees.detectScript(surn)) {
- // Same script as NAME field?
- msurn = name.replace(/\/.*\//, "/" + val + "/");
- } else if (heb !== "" && webtrees.detectScript(val) === webtrees.detectScript(heb)) {
- // Same script as _HEB field?
- msurn = heb.replace(/\/.*\//, "/" + val + "/");
- } else if (romn !== "" && webtrees.detectScript(val) === webtrees.detectScript(romn)) {
- //. Same script as ROMN field
- msurn = romn.replace(/\/.*\//, "/" + val + "/");
- }
- }
- document.getElementById(marnm_id).value = msurn;
- document.getElementById(marnm_id + "_display").innerHTML = msurn;
- } else {
- marnm_id = ip[i].id;
- }
- }
- }
- }
-
- // Toggle the name editor fields between
- // <input type="hidden"> <span style="display:inline">
- // <input type="text"> <span style="display:none">
-
- var oldName = "";
-
- // Calls to generate_name() trigger an update - hence need to
- // set the manual change to true first. We are probably
- // listening to the wrong events on the input fields...
- var manualChange = generate_name() !== NAME.val();
-
- function convertHidden(eid) {
- var input1 = $("#" + eid);
- var input2 = $("#" + eid + "_display");
-
- if (input1.attr("type") === "hidden") {
- input1.attr("type", "text");
- input2.hide();
- } else {
- input1.attr("type", "hidden");
- input2.show();
- }
- }
-
- /**
- * if the user manually changed the NAME field, then update the textual
- * HTML representation of it
- * If the value changed set manualChange to true so that changing
- * the other fields doesn’t change the NAME line
- */
- function updateTextName(eid) {
- var element = document.getElementById(eid);
- if (element) {
- if (element.value !== oldName) {
- manualChange = true;
- }
- var delement = document.getElementById(eid + "_display");
- if (delement) {
- delement.innerHTML = element.value;
- }
- }
- }
-
- function checkform() {
- var ip = document.getElementsByTagName("input");
- for (var i = 0; i < ip.length; i++) {
- // ADD slashes to _HEB and _AKA names
- if (ip[i].id.indexOf("_AKA") === 0 || ip[i].id.indexOf("_HEB") === 0 || ip[i].id.indexOf("ROMN") === 0)
- if (ip[i].value.indexOf("/") < 0 && ip[i].value !== "")
- ip[i].value = ip[i].value.replace(/([^\s]+)\s*$/, "/$1/");
- // Blank out temporary _MARNM_SURN
- if (ip[i].id.indexOf("_MARNM_SURN") === 0)
- ip[i].value = "";
- // Convert "xxx yyy" and "xxx y yyy" surnames to "xxx,yyy"
- if ((SURNAME_TRADITION === "spanish" || "SURNAME_TRADITION" === "portuguese") && ip[i].id.indexOf("SURN") === 0) {
- ip[i].value = document.forms[0].SURN.value.replace(/^\s*([^\s,]{2,})\s+([iIyY] +)?([^\s,]{2,})\s*$/, "$1,$3");
- }
- }
- return true;
- }
-
- // If the name isn’t initially formed from the components in a standard way,
- // then don’t automatically update it.
- if (NAME.val() !== generate_name() && NAME.val() !== "//") {
- convertHidden(NAME.attr("id"));
- }
-</script>
-<?php View::endpush() ?>
-
diff --git a/resources/views/family-page-children.phtml b/resources/views/family-page-children.phtml
index 7920931dab..06907a4ce4 100644
--- a/resources/views/family-page-children.phtml
+++ b/resources/views/family-page-children.phtml
@@ -37,11 +37,11 @@ use Fisharebest\Webtrees\I18N;
<?php endforeach ?>
<?php if ($family->canEdit()) : ?>
<div class="wt-chart-box">
- <a class="btn btn-link" href="<?= e(route(AddChildToFamilyPage::class, ['gender' => 'M', 'tree' => $family->tree()->name(), 'xref' => $family->xref()])) ?>">
+ <a class="btn btn-link" href="<?= e(route(AddChildToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'M'])) ?>">
<?= I18N::translate('Add a son') ?>
</a>
|
- <a class="btn btn-link" href="<?= e(route(AddChildToFamilyPage::class, ['gender' => 'F', 'tree' => $family->tree()->name(), 'xref' => $family->xref()])) ?>">
+ <a class="btn btn-link" href="<?= e(route(AddChildToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'F'])) ?>">
<?= I18N::translate('Add a daughter') ?>
</a>
<br>
diff --git a/resources/views/family-page-grandparents.phtml b/resources/views/family-page-grandparents.phtml
index 75611764d4..da736c70a5 100644
--- a/resources/views/family-page-grandparents.phtml
+++ b/resources/views/family-page-grandparents.phtml
@@ -7,8 +7,9 @@ use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
/**
- * @var Individual|null $individual
- * @var Family|null $family
+ * @var Family $family
+ * @var Individual $individual
+ * @var Family|null $parent_family
*/
?>
@@ -26,24 +27,24 @@ use Fisharebest\Webtrees\Individual;
<div class="align-self-center">
<?php if ($individual === null) : ?>
<?= view('chart-box', ['individual' => null]) ?>
- <?php elseif ($family === null) : ?>
+ <?php elseif ($parent_family === null) : ?>
<div class="wt-chart-box">
<?php if ($individual->canEdit()) : ?>
- <a class="btn btn-link" href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'gender' => 'M'])) ?>">
+ <a class="btn btn-link" href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'sex' => 'M', 'url' => $family->url()])) ?>">
<?= I18N::translate('Add a father') ?>
</a>
<?php endif ?>
</div>
- <?php elseif ($family->husband() === null) : ?>
+ <?php elseif ($parent_family->husband() === null) : ?>
<div class="wt-chart-box">
- <?php if ($family->canEdit()) : ?>
- <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'famtag' => 'HUSB'])) ?>">
+ <?php if ($parent_family->canEdit()) : ?>
+ <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $parent_family->tree()->name(), 'xref' => $parent_family->xref(), 'sex' => 'M', 'url' => $family->url()])) ?>">
<?= I18N::translate('Add a father') ?>
</a>
<?php endif ?>
</div>
<?php else : ?>
- <?= view('chart-box', ['individual' => $family ? $family->husband() : null]) ?>
+ <?= view('chart-box', ['individual' => $parent_family ? $parent_family->husband() : null]) ?>
<?php endif ?>
</div>
</div>
@@ -56,31 +57,31 @@ use Fisharebest\Webtrees\Individual;
<div class="align-self-center">
<?php if ($individual === null) : ?>
<?= view('chart-box', ['individual' => null]) ?>
- <?php elseif ($family === null) : ?>
+ <?php elseif ($parent_family === null) : ?>
<div class="wt-chart-box">
<?php if ($individual->canEdit()) : ?>
- <a class="btn btn-link" href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'gender' => 'F'])) ?>">
+ <a class="btn btn-link" href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'sex' => 'F', 'url' => $family->url()])) ?>">
<?= I18N::translate('Add a mother') ?>
</a>
<?php endif ?>
</div>
- <?php elseif ($family->wife() === null) : ?>
+ <?php elseif ($parent_family->wife() === null) : ?>
<div class="wt-chart-box">
- <?php if ($family->canEdit()) : ?>
- <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'famtag' => 'WIFE'])) ?>">
+ <?php if ($parent_family->canEdit()) : ?>
+ <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $parent_family->tree()->name(), 'xref' => $parent_family->xref(), 'sex' => 'F', 'url' => $family->url()])) ?>">
<?= I18N::translate('Add a mother') ?>
</a>
<?php endif ?>
</div>
<?php else : ?>
- <?= view('chart-box', ['individual' => $family ? $family->wife() : null]) ?>
+ <?= view('chart-box', ['individual' => $parent_family ? $parent_family->wife() : null]) ?>
<?php endif ?>
</div>
</div>
</div>
- <?php if ($family !== null) : ?>
+ <?php if ($parent_family !== null) : ?>
<div class="align-self-center">
- <a class="btn btn-text" href="<?= e($family->url()) ?>" title="<?= strip_tags($family->fullName()) ?>">
+ <a class="btn btn-text" href="<?= e($parent_family->url()) ?>" title="<?= strip_tags($parent_family->fullName()) ?>">
<?= view('icons/arrow-right') ?>
</a>
</div>
diff --git a/resources/views/family-page-menu.phtml b/resources/views/family-page-menu.phtml
index dcafc5c357..5359b10a19 100644
--- a/resources/views/family-page-menu.phtml
+++ b/resources/views/family-page-menu.phtml
@@ -29,20 +29,20 @@ use Fisharebest\Webtrees\I18N;
</a>
<?php if ($record->husband() === null) : ?>
- <a class="dropdown-item" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref(), 'famtag' => 'HUSB'])) ?>">
+ <a class="dropdown-item" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref(), 'sex' => 'M'])) ?>">
<?= view('icons/add') ?>
<?= I18N::translate('Add a husband') ?>
</a>
<?php endif ?>
<?php if ($record->wife() === null) : ?>
- <a class="dropdown-item" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref(), 'famtag' => 'WIFE'])) ?>">
+ <a class="dropdown-item" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref(), 'sex' => 'F'])) ?>">
<?= view('icons/add') ?>
<?= I18N::translate('Add a wife') ?>
</a>
<?php endif ?>
- <a class="dropdown-item" href="<?= e(route(AddChildToFamilyPage::class, ['gender' => 'U', 'tree' => $record->tree()->name(), 'xref' => $record->xref()])) ?>">
+ <a class="dropdown-item" href="<?= e(route(AddChildToFamilyPage::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref(), 'sex' => 'U'])) ?>">
<?= view('icons/add') ?>
<?= I18N::translate('Add a child') ?>
</a>
diff --git a/resources/views/family-page-parents.phtml b/resources/views/family-page-parents.phtml
index 1cb009b2af..f72876f591 100644
--- a/resources/views/family-page-parents.phtml
+++ b/resources/views/family-page-parents.phtml
@@ -23,7 +23,7 @@ use Fisharebest\Webtrees\Individual;
<?php else : ?>
<div class="wt-chart-box">
<?php if ($family->canEdit()) : ?>
- <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'famtag' => 'HUSB'])) ?>">
+ <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'M'])) ?>">
<?= I18N::translate('Add a husband')?>
</a>
<?php endif ?>
@@ -31,7 +31,7 @@ use Fisharebest\Webtrees\Individual;
<?php endif ?>
</div>
- <?= view('family-page-grandparents', ['family' => $family->husband() ? $family->husband()->childFamilies()->first() : null, 'individual' => $family->husband()]) ?>
+ <?= view('family-page-grandparents', ['family' => $family, 'parent_family' => $family->husband() ? $family->husband()->childFamilies()->first() : null, 'individual' => $family->husband()]) ?>
</div>
<div class="d-flex">
@@ -45,7 +45,7 @@ use Fisharebest\Webtrees\Individual;
<?php else : ?>
<div class="wt-chart-box">
<?php if ($family->canEdit()) : ?>
- <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'famtag' => 'WIFE'])) ?>">
+ <a class="btn btn-link" href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'F'])) ?>">
<?= I18N::translate('Add a wife')?>
</a>
<?php endif ?>
@@ -53,7 +53,7 @@ use Fisharebest\Webtrees\Individual;
<?php endif ?>
</div>
- <?= view('family-page-grandparents', ['family' => $family->wife() ? $family->wife()->childFamilies()->first() : null, 'individual' => $family->wife()]) ?>
+ <?= view('family-page-grandparents', ['family' => $family, 'parent_family' => $family->wife() ? $family->wife()->childFamilies()->first() : null, 'individual' => $family->wife()]) ?>
</div>
</div>
</div>
diff --git a/resources/views/individual-name.phtml b/resources/views/individual-name.phtml
index 4c3d32792e..a6ca658ae6 100644
--- a/resources/views/individual-name.phtml
+++ b/resources/views/individual-name.phtml
@@ -6,7 +6,7 @@ use Fisharebest\Webtrees\Functions\FunctionsPrint;
use Fisharebest\Webtrees\Functions\FunctionsPrintFacts;
use Fisharebest\Webtrees\Http\RequestHandlers\CopyFact;
use Fisharebest\Webtrees\Http\RequestHandlers\DeleteFact;
-use Fisharebest\Webtrees\Http\RequestHandlers\EditName;
+use Fisharebest\Webtrees\Http\RequestHandlers\EditFactPage;
use Fisharebest\Webtrees\I18N;
/**
@@ -43,7 +43,7 @@ if ($fact->isPendingDeletion()) {
<?= $fake_individual->fullName() ?>
<?php if ($fact->attribute('TYPE') !== '') : ?>
- <?= Registry::elementFactory()->make('INDI:NAME:TYPE')->value($fact->attribute('TYPE'), $tree) ?>
+ <?= Registry::elementFactory()->make($fact->tag() . ':TYPE')->value($fact->attribute('TYPE'), $tree) ?>
<?php endif ?>
</a>
</div>
@@ -56,16 +56,13 @@ if ($fact->isPendingDeletion()) {
<?php preg_match_all('/\n2 (\w+) (.+)/', $fact->gedcom(), $matches, PREG_SET_ORDER) ?>
<?php foreach ($matches as $key => $match) : ?>
<?php [, $tag, $value] = $match ?>
+ <?php $element = Registry::elementFactory()->make($fact->tag() . ':' . $tag) ?>
<?php if ($tag !== 'SOUR' && $tag !== 'NOTE') : ?>
<dt class="col-md-4 col-lg-3">
- <?= Registry::elementFactory()->make($fact->tag() . ':' . $tag)->label() ?>
+ <?= $element->label() ?>
</dt>
<dd class="col-md-8 col-lg-9">
- <?php if ($tag === 'TYPE') : ?>
- <?= Registry::elementFactory()->make('INDI:NAME:TYPE')->value($value, $tree) ?>
- <?php else: ?>
- <span dir="auto"><?= e($value) ?></span>
- <?php endif ?>
+ <?= $element->value($value, $fact->record()->tree()) ?>
</dd>
<?php endif ?>
<?php endforeach ?>
@@ -76,7 +73,7 @@ if ($fact->isPendingDeletion()) {
<?php if ($fact->canEdit()) : ?>
<div class="d-flex">
- <a class="btn btn-link ml-auto" href="<?= e(route(EditName::class, ['xref' => $individual->xref(), 'fact_id' => $fact->id(), 'tree' => $individual->tree()->name()])) ?>" title="<?= I18N::translate('Edit the name') ?>">
+ <a class="btn btn-link ml-auto" href="<?= e(route(EditFactPage::class, ['xref' => $individual->xref(), 'fact_id' => $fact->id(), 'tree' => $individual->tree()->name()])) ?>" title="<?= I18N::translate('Edit the name') ?>">
<?= view('icons/edit') ?>
<span class="sr-only"><?= I18N::translate('Edit the name') ?></span>
</a>
diff --git a/resources/views/individual-page-menu.phtml b/resources/views/individual-page-menu.phtml
index a22bc6a946..e93b576dd7 100644
--- a/resources/views/individual-page-menu.phtml
+++ b/resources/views/individual-page-menu.phtml
@@ -1,7 +1,6 @@
<?php
use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Http\RequestHandlers\AddName;
use Fisharebest\Webtrees\Http\RequestHandlers\AddNewFact;
use Fisharebest\Webtrees\Http\RequestHandlers\DeleteRecord;
use Fisharebest\Webtrees\Http\RequestHandlers\EditFactPage;
@@ -36,7 +35,7 @@ use Illuminate\Support\Collection;
<hr>
<?php endif ?>
- <a class="dropdown-item" href="<?= e(route(AddName::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref()])) ?>">
+ <a class="dropdown-item" href="<?= e(route(AddNewFact::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref(), 'fact' => 'NAME'])) ?>">
<?= view('icons/add') ?>
<?= I18N::translate('Add a name') ?>
</a>
diff --git a/resources/views/media-page-menu.phtml b/resources/views/media-page-menu.phtml
index 2ad7e9459b..8268e1360a 100644
--- a/resources/views/media-page-menu.phtml
+++ b/resources/views/media-page-menu.phtml
@@ -1,8 +1,7 @@
<?php
-use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Http\RequestHandlers\DeleteRecord;
-use Fisharebest\Webtrees\Http\RequestHandlers\EditRawRecordPage;
+use Fisharebest\Webtrees\Http\RequestHandlers\EditRecordPage;
use Fisharebest\Webtrees\Http\RequestHandlers\LinkMediaToFamilyModal;
use Fisharebest\Webtrees\Http\RequestHandlers\LinkMediaToIndividualModal;
use Fisharebest\Webtrees\Http\RequestHandlers\LinkMediaToSourceModal;
@@ -22,6 +21,11 @@ use Fisharebest\Webtrees\Media;
</button>
<div class="dropdown-menu dropdown-menu-right wt-page-menu-items" aria-labelledby="page-menu">
+ <a class="dropdown-item" href="<?= route(EditRecordPage::class, ['xref' => $record->xref(), 'tree' => $record->tree()->name()]) ?>">
+ <?= view('icons/edit') ?>
+ <?= I18N::translate('Edit') ?>
+ </a>
+
<a class="dropdown-item" href="#" data-href="<?= e(route(LinkMediaToIndividualModal::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref()])) ?>" data-target="#wt-ajax-modal" data-toggle="modal" data-backdrop="static">
<?= view('icons/link') ?>
<?= I18N::translate('Link this media object to an individual') ?>
@@ -43,12 +47,5 @@ use Fisharebest\Webtrees\Media;
<?= view('icons/delete') ?>
<?= I18N::translate('Delete') ?>
</a>
-
- <?php if (Auth::isAdmin() || $record->tree()->getPreference('SHOW_GEDCOM_RECORD')) : ?>
- <a class="dropdown-item" href="<?= e(route(EditRawRecordPage::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref()])) ?>">
- <?= view('icons/edit') ?>
- <?= I18N::translate('Edit the raw GEDCOM') ?>
- </a>
- <?php endif ?>
</div>
</div>
diff --git a/resources/views/media-page.phtml b/resources/views/media-page.phtml
index fbc6e65bbe..2593c61a71 100644
--- a/resources/views/media-page.phtml
+++ b/resources/views/media-page.phtml
@@ -1,10 +1,9 @@
<?php
use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Fact;
-use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
use Fisharebest\Webtrees\Functions\FunctionsPrintFacts;
+use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\Http\RequestHandlers\AddMediaFileModal;
use Fisharebest\Webtrees\Http\RequestHandlers\AddNewFact;
use Fisharebest\Webtrees\Http\RequestHandlers\DeleteFact;
@@ -12,25 +11,22 @@ use Fisharebest\Webtrees\Http\RequestHandlers\EditMediaFileModal;
use Fisharebest\Webtrees\Http\RequestHandlers\PendingChangesAcceptRecord;
use Fisharebest\Webtrees\Http\RequestHandlers\PendingChangesRejectRecord;
use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Media;
-use Fisharebest\Webtrees\Note;
use Fisharebest\Webtrees\Registry;
-use Fisharebest\Webtrees\Source;
use Fisharebest\Webtrees\Tree;
use Illuminate\Support\Collection;
-use League\Flysystem\FilesystemInterface;
+use League\Flysystem\FilesystemOperator;
/**
- * @var Collection<Fact> $clipboard_facts
- * @var FilesystemInterface $data_filesystem
- * @var Collection<Fact> $facts
- * @var Collection<Family> $families
- * @var Collection<Individual> $individuals
- * @var Media $media
- * @var Collection<Note> $notes
- * @var Collection<Source> $sources
- * @var Tree $tree
+ * @var Collection $clipboard_facts
+ * @var FilesystemOperator $data_filesystem
+ * @var Collection $facts
+ * @var Collection $families
+ * @var Collection $individuals
+ * @var Media $media
+ * @var Collection $notes
+ * @var Collection $sources
+ * @var Tree $tree
*/
?>
@@ -98,7 +94,7 @@ use League\Flysystem\FilesystemInterface;
<div class="tab-pane active fade show" role="tabpanel" id="details">
<table class="table wt-facts-table">
<?php foreach ($media->mediaFiles() as $media_file) : ?>
- <tr class="<?= $media_file->isPendingAddition() ? 'wt-new' : '' ?> <?= $media_file->isPendingDeletion() ? 'wt-old' : '' ?>">
+ <tr class="<?= $media_file->isPendingAddition() ? 'wt-new' : '' ?><?= $media_file->isPendingDeletion() ? 'wt-old' : '' ?>">
<th scope="row">
<?= I18N::translate('Media file') ?>
<?php if ($media->canEdit()) : ?>
diff --git a/resources/views/modules/custom-tags/config.phtml b/resources/views/modules/custom-tags/config.phtml
index 905a75198f..467bada86c 100644
--- a/resources/views/modules/custom-tags/config.phtml
+++ b/resources/views/modules/custom-tags/config.phtml
@@ -1,11 +1,14 @@
<?php
+use Fisharebest\Webtrees\Contracts\ElementFactoryInterface;
use Fisharebest\Webtrees\Contracts\ElementInterface;
use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesAllPage;
use Fisharebest\Webtrees\I18N;
/**
+ * @var ElementFactoryInterface $element_factory
+ * @var array<string,array<string>> $subtags
* @var array<string,ElementInterface> $tags
* @var string $title
*/
@@ -16,24 +19,58 @@ use Fisharebest\Webtrees\I18N;
<h1><?= $title ?></h1>
+<h2><?= I18N::translate('Display custom GEDCOM tags') ?></h2>
+
<table class="table table-bordered">
- <caption class="sr-only"><?= I18N::translate('Custom tags') ?></caption>
+ <caption class="sr-only"><?= I18N::translate('Custom GEDCOM tags') ?></caption>
+
<thead>
<tr>
- <th><?= I18N::translate('Custom tag') ?></th>
+ <th><?= I18N::translate('Custom GEDCOM tag') ?></th>
<th><?= I18N::translate('Label') ?></th>
</tr>
</thead>
+
<tbody>
<?php foreach ($tags as $tag => $element) : ?>
<tr>
- <td><code><?= $tag ?></code></td>
+ <td dir="ltr"><code><?= $tag ?></code></td>
<td><?= $element->label() ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
+<h2><?= I18N::translate('Add/remove GEDCOM tags in the edit forms') ?></h2>
+
+<table class="table table-bordered">
+ <caption class="sr-only"><?= I18N::translate('Custom sub-tags') ?></caption>
+
+ <thead>
+ <tr>
+ <th><?= I18N::translate('GEDCOM tag') ?></th>
+ <th><?= I18N::translate('Label') ?></th>
+ <th><?= I18N::translate('GEDCOM sub-tag') ?></th>
+ <th><?= I18N::translate('Label') ?></th>
+ <th><?= I18N::translate('Count') ?></th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <?php foreach ($subtags as $tag => $children) : ?>
+ <?php foreach ($children as $child) : ?>
+ <tr>
+ <td dir="ltr"><code><?= $tag ?></code></td>
+ <td><?= $element_factory->make($tag)->label() ?></td>
+ <td dir="ltr"><code><?= $child[0] ?></code></td>
+ <td><?= $element_factory->make($tag . ':' . $child[0])->label() ?></td>
+ <td><?= $child[1] ?? '0:1' ?></td>
+ </tr>
+ <?php endforeach ?>
+ <?php endforeach ?>
+ </tbody>
+</table>
+
<p>
<a href="<?= e(route(ControlPanel::class)) ?>" class="btn btn-primary">
<?= view('icons/save') ?>
diff --git a/resources/views/modules/relatives/family.phtml b/resources/views/modules/relatives/family.phtml
index f961502611..562f3908c3 100644
--- a/resources/views/modules/relatives/family.phtml
+++ b/resources/views/modules/relatives/family.phtml
@@ -59,7 +59,7 @@ use Fisharebest\Webtrees\Services\RelationshipService;
<tr>
<th scope="row"></th>
<td>
- <a href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'famtag' => 'HUSB'])) ?>">
+ <a href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'M', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= I18N::translate('Add a husband') ?>
</a>
</td>
@@ -96,7 +96,7 @@ use Fisharebest\Webtrees\Services\RelationshipService;
<tr>
<th scope="row"></th>
<td>
- <a href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'famtag' => 'WIFE'])) ?>">
+ <a href="<?= e(route(AddSpouseToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'F', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= I18N::translate('Add a wife') ?>
</a>
</td>
@@ -216,15 +216,15 @@ use Fisharebest\Webtrees\Services\RelationshipService;
</th>
<td>
- <a href="<?= e(route(AddChildToFamilyPage::class, ['gender' => 'M', 'tree' => $family->tree()->name(), 'xref' => $family->xref()])) ?>">
+ <a href="<?= e(route(AddChildToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'M', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= $type === 'FAMS' ? I18N::translate('Add a son') : I18N::translate('Add a brother') ?>
</a>
|
- <a href="<?= e(route(AddChildToFamilyPage::class, ['gender' => 'F', 'tree' => $family->tree()->name(), 'xref' => $family->xref()])) ?>">
+ <a href="<?= e(route(AddChildToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'F', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= $type === 'FAMS' ? I18N::translate('Add a daughter') : I18N::translate('Add a sister') ?>
</a>
|
- <a href="<?= e(route(AddChildToFamilyPage::class, ['gender' => 'U', 'tree' => $family->tree()->name(), 'xref' => $family->xref()])) ?>">
+ <a href="<?= e(route(AddChildToFamilyPage::class, ['tree' => $family->tree()->name(), 'xref' => $family->xref(), 'sex' => 'U', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= $type === 'FAMS' ? I18N::translate('Add a child') : I18N::translate('Add a sibling') ?>
</a>
diff --git a/resources/views/modules/relatives/tab.phtml b/resources/views/modules/relatives/tab.phtml
index 5a4f610e94..9ecb4f0e46 100644
--- a/resources/views/modules/relatives/tab.phtml
+++ b/resources/views/modules/relatives/tab.phtml
@@ -45,14 +45,14 @@ use Illuminate\Support\Collection;
<tbody>
<tr>
<td>
- <a href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'gender' => 'M'])) ?>">
+ <a href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'sex' => 'M', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= I18N::translate('Add a father') ?>
</a>
</td>
</tr>
<tr>
<td>
- <a href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'gender' => 'F'])) ?>">
+ <a href="<?= e(route(AddParentToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'sex' => 'F', 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?= I18N::translate('Add a mother') ?>
</a>
</td>
@@ -129,7 +129,7 @@ use Illuminate\Support\Collection;
<tr>
<td>
- <a href="<?= e(route(AddSpouseToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref()])) ?>">
+ <a href="<?= e(route(AddSpouseToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'url' => $individual->url() . '#tab-relatives'])) ?>">
<?php if ($individual->sex() !== 'F') : ?>
<?= I18N::translate('Add a wife') ?>
<?php else : ?>
@@ -153,7 +153,7 @@ use Illuminate\Support\Collection;
<tr>
<td>
- <a href="<?= e(route(AddChildToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'gender' => 'U'])) ?>">
+ <a href="<?= e(route(AddChildToIndividualPage::class, ['tree' => $individual->tree()->name(), 'xref' => $individual->xref(), 'sex' => 'U'])) ?>">
<?= I18N::translate('Add a child to create a one-parent family') ?>
</a>
</td>
diff --git a/tests/.DS_Store b/tests/.DS_Store
new file mode 100644
index 0000000000..3d3d369c11
--- /dev/null
+++ b/tests/.DS_Store
Binary files differ