summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Contracts/ElementInterface.php4
-rw-r--r--app/Elements/AbstractElement.php29
-rw-r--r--app/Elements/Change.php4
-rw-r--r--app/Elements/ChangeDate.php4
-rw-r--r--app/Elements/FamilyRecord.php1
-rw-r--r--app/Elements/FamilyStatusText.php7
-rw-r--r--app/Elements/IndividualRecord.php1
-rw-r--r--app/Elements/MediaRecord.php2
-rw-r--r--app/Elements/NamePersonal.php55
-rw-r--r--app/Elements/NamePhoneticVariation.php12
-rw-r--r--app/Elements/NameRomanizedVariation.php12
-rw-r--r--app/Elements/NoteRecord.php1
-rw-r--r--app/Elements/RepositoryRecord.php1
-rw-r--r--app/Elements/SourceRecord.php1
-rw-r--r--app/Elements/XrefRepository.php3
-rw-r--r--app/Factories/ElementFactory.php708
-rw-r--r--app/Http/RequestHandlers/ControlPanel.php3
-rw-r--r--app/Http/RequestHandlers/ModulesCustomTagsAction.php50
-rw-r--r--app/Http/RequestHandlers/ModulesCustomTagsPage.php47
-rw-r--r--app/Http/Routes/WebRoutes.php4
-rw-r--r--app/Module/CustomTagsAldfaer.php56
-rw-r--r--app/Module/CustomTagsAncestry.php55
-rw-r--r--app/Module/CustomTagsBrothersKeeper.php117
-rw-r--r--app/Module/CustomTagsFamilySearch.php53
-rw-r--r--app/Module/CustomTagsFamilyTreeBuilder.php64
-rw-r--r--app/Module/CustomTagsFamilyTreeMaker.php148
-rw-r--r--app/Module/CustomTagsGedcom53.php191
-rw-r--r--app/Module/CustomTagsGedcom55.php53
-rw-r--r--app/Module/CustomTagsGedcomL.php248
-rw-r--r--app/Module/CustomTagsLegacy.php138
-rw-r--r--app/Module/CustomTagsPersonalAncestralFile.php67
-rw-r--r--app/Module/CustomTagsPhpGedView.php95
-rw-r--r--app/Module/CustomTagsReunion.php59
-rw-r--r--app/Module/CustomTagsRootsMagic.php59
-rw-r--r--app/Module/CustomTagsWebtrees.php124
-rw-r--r--app/Module/ModuleCustomTagsInterface.php45
-rw-r--r--app/Module/ModuleCustomTagsTrait.php113
-rw-r--r--app/Services/ModuleService.php28
-rw-r--r--resources/views/admin/control-panel.phtml11
-rw-r--r--resources/views/edit/edit-fact.phtml19
-rw-r--r--resources/views/edit/edit-gedcom-fields.phtml2
-rw-r--r--resources/views/edit/edit-record.phtml23
-rw-r--r--resources/views/modules/custom-tags/config.phtml42
-rw-r--r--resources/views/repository-page-menu.phtml15
-rw-r--r--resources/views/source-page-menu.phtml15
45 files changed, 1978 insertions, 811 deletions
diff --git a/app/Contracts/ElementInterface.php b/app/Contracts/ElementInterface.php
index d4bd3283d0..145e3a50f4 100644
--- a/app/Contracts/ElementInterface.php
+++ b/app/Contracts/ElementInterface.php
@@ -87,11 +87,11 @@ interface ElementInterface
*
* @param string $subtag
* @param string $repeat
- * @param string $after
+ * @param string $before
*
* @return void
*/
- public function subtag(string $subtag, string $repeat = '', string $after = ''): void;
+ public function subtag(string $subtag, string $repeat, string $before): void;
/**
* @return array<string,string>
diff --git a/app/Elements/AbstractElement.php b/app/Elements/AbstractElement.php
index 186049fa09..1886fa85bc 100644
--- a/app/Elements/AbstractElement.php
+++ b/app/Elements/AbstractElement.php
@@ -26,11 +26,11 @@ use Fisharebest\Webtrees\Tree;
use function array_key_exists;
use function array_map;
-use function array_search;
use function e;
use function is_numeric;
use function preg_match;
-use function strpos;
+use function str_contains;
+use function strip_tags;
use function trim;
use function view;
@@ -48,11 +48,11 @@ abstract class AbstractElement implements ElementInterface
// Which child elements can appear under this element.
protected const SUBTAGS = [];
- /** @var string A label to describe this element */
- private $label;
+ // A label to describe this element
+ private string $label;
- /** @var array<string,string> */
- private $subtags;
+ /** @var array<string,string> Subtags of this element */
+ private array $subtags;
/**
* AbstractGedcomElement constructor.
@@ -77,7 +77,7 @@ abstract class AbstractElement implements ElementInterface
{
$value = strtr($value, ["\t" => ' ', "\r" => ' ', "\n" => ' ']);
- while (strpos($value, ' ') !== false) {
+ while (str_contains($value, ' ')) {
$value = strtr($value, [' ' => ' ']);
}
@@ -119,7 +119,7 @@ abstract class AbstractElement implements ElementInterface
}
// We may use markup to display values, but not when editing them.
- $values = array_map('strip_tags', $values);
+ $values = array_map(fn(string $x): string => strip_tags($x), $values);
return view('components/select', [
'id' => $id,
@@ -214,25 +214,24 @@ abstract class AbstractElement implements ElementInterface
*
* @param string $subtag
* @param string $repeat
- * @param string $after
+ * @param string $before
*
* @return void
*/
- public function subtag(string $subtag, string $repeat = '', string $after = ''): void
+ public function subtag(string $subtag, string $repeat, string $before): void
{
if ($repeat === '') {
unset($this->subtags[$subtag]);
- } elseif ($after === '' || ($this->subtags[$subtag] ?? null) === null) {
+ } elseif ($before === '' || ($this->subtags[$before] ?? null) === null) {
$this->subtags[$subtag] = $repeat;
} else {
$tmp = [];
foreach ($this->subtags as $key => $value) {
- $tmp[$key] = $value;
-
- if ($key === $after) {
- $tmp[] = $repeat;
+ if ($key === $before) {
+ $tmp[$key] = $repeat;
}
+ $tmp[$key] = $value;
}
$this->subtags = $tmp;
diff --git a/app/Elements/Change.php b/app/Elements/Change.php
index a7889d6d89..955f605c4e 100644
--- a/app/Elements/Change.php
+++ b/app/Elements/Change.php
@@ -24,4 +24,8 @@ namespace Fisharebest\Webtrees\Elements;
*/
class Change extends EmptyElement
{
+ protected const SUBTAGS = [
+ 'DATE' => '1:1',
+ 'NOTE' => '0:M',
+ ];
}
diff --git a/app/Elements/ChangeDate.php b/app/Elements/ChangeDate.php
index 767e8e063f..63d16edd8c 100644
--- a/app/Elements/ChangeDate.php
+++ b/app/Elements/ChangeDate.php
@@ -29,6 +29,10 @@ use Fisharebest\Webtrees\Tree;
*/
class ChangeDate extends AbstractElement
{
+ protected const SUBTAGS = [
+ 'TIME' => '1:1',
+ ];
+
/**
* Display the value of this type of element.
*
diff --git a/app/Elements/FamilyRecord.php b/app/Elements/FamilyRecord.php
index 8a0ce7c55f..9136edfb12 100644
--- a/app/Elements/FamilyRecord.php
+++ b/app/Elements/FamilyRecord.php
@@ -26,6 +26,7 @@ class FamilyRecord extends AbstractElement
{
protected const SUBTAGS = [
'MARR' => '0:1',
+ 'RESN' => '0:1',
'CHAN' => '0:1',
];
}
diff --git a/app/Elements/FamilyStatusText.php b/app/Elements/FamilyStatusText.php
index e230e7e0ce..903cce60e6 100644
--- a/app/Elements/FamilyStatusText.php
+++ b/app/Elements/FamilyStatusText.php
@@ -33,6 +33,13 @@ use function strtoupper;
*/
class FamilyStatusText extends AbstractElement
{
+ protected const SUBTAGS = [
+ 'DATE' => '0:1',
+ 'PLAC' => '0:1',
+ 'NOTE' => '0:M',
+ 'SOUR' => '0:M',
+ ];
+
/**
* Convert a value to a canonical form.
*
diff --git a/app/Elements/IndividualRecord.php b/app/Elements/IndividualRecord.php
index 036a69d65c..db2693a9c9 100644
--- a/app/Elements/IndividualRecord.php
+++ b/app/Elements/IndividualRecord.php
@@ -28,6 +28,7 @@ class IndividualRecord extends AbstractElement
'SEX' => '1:1',
'BIRT' => '0:1',
'DEAT' => '0:1',
+ 'RESN' => '0:1',
'CHAN' => '0:1',
];
}
diff --git a/app/Elements/MediaRecord.php b/app/Elements/MediaRecord.php
index ec597bcf04..22eff9fea3 100644
--- a/app/Elements/MediaRecord.php
+++ b/app/Elements/MediaRecord.php
@@ -30,7 +30,7 @@ class MediaRecord extends AbstractElement
'RIN' => '0:1',
'NOTE' => '0:M',
'SOUR' => '0:M',
+ 'RESN' => '0:1',
'CHAN' => '0:1',
- 'RESN' => '0:1', /* *** webtrees extension */
];
}
diff --git a/app/Elements/NamePersonal.php b/app/Elements/NamePersonal.php
index 31af63c8e0..deda8f934b 100644
--- a/app/Elements/NamePersonal.php
+++ b/app/Elements/NamePersonal.php
@@ -24,6 +24,7 @@ use Fisharebest\Webtrees\SurnameTradition;
use Fisharebest\Webtrees\Tree;
use function e;
+use function in_array;
use function view;
/**
@@ -61,6 +62,27 @@ class NamePersonal extends AbstractElement
'SURN' => '0:1',
'NSFX' => '0:1',
'NICK' => '0:1',
+ 'NOTE' => '0:M',
+ 'SOUR' => '0:M',
+ 'FONE' => '0:M',
+ 'ROMN' => '0:M',
+ ];
+
+ // For some languages, we want to show the surname field first.
+ protected const SURNAME_FIRST_LANGUAGES = ['hu', 'jp', 'ko', 'zh-Hans', 'zh-Hant'];
+
+ protected const SUBTAGS_SURNAME_FIRST = [
+ 'TYPE' => '0:1',
+ 'NPFX' => '0:1',
+ 'SPFX' => '0:1',
+ 'SURN' => '0:1',
+ 'GIVN' => '0:1',
+ 'NSFX' => '0:1',
+ 'NICK' => '0:1',
+ 'NOTE' => '0:M',
+ 'SOUR' => '0:M',
+ 'FONE' => '0:M',
+ 'ROMN' => '0:M',
];
/**
@@ -107,37 +129,10 @@ class NamePersonal extends AbstractElement
*/
public function subtags(): array
{
- $language = I18N::languageTag();
-
- switch ($language) {
- case 'hu':
- case 'jp':
- case 'ko':
- case 'zh-Hans':
- case 'zh-Hant':
- $subtags = [
- 'TYPE' => '0:1',
- 'NPFX' => '0:1',
- 'SPFX' => '0:1',
- 'SURN' => '0:1',
- 'GIVN' => '0:1',
- 'NSFX' => '0:1',
- 'NICK' => '0:1',
- ];
- break;
- default:
- $subtags = [
- 'TYPE' => '0:1',
- 'NPFX' => '0:1',
- 'GIVN' => '0:1',
- 'SPFX' => '0:1',
- 'SURN' => '0:1',
- 'NSFX' => '0:1',
- 'NICK' => '0:1',
- ];
- break;
+ if (in_array(I18N::languageTag(), static::SURNAME_FIRST_LANGUAGES, true)) {
+ return static::SUBTAGS_SURNAME_FIRST;
}
- return $subtags;
+ return static::SUBTAGS;
}
}
diff --git a/app/Elements/NamePhoneticVariation.php b/app/Elements/NamePhoneticVariation.php
index cdf4da01d8..6e9c2bf042 100644
--- a/app/Elements/NamePhoneticVariation.php
+++ b/app/Elements/NamePhoneticVariation.php
@@ -29,7 +29,15 @@ namespace Fisharebest\Webtrees\Elements;
*/
class NamePhoneticVariation extends AbstractElement
{
- protected const SUBTAGS = ['NPFX', 'GIVN', 'SPFX','SURN', 'NSFX', 'NICK', 'TYPE'];
-
protected const MAXIMUM_LENGTH = 120;
+
+ protected const SUBTAGS = [
+ 'TYPE' => '0:1',
+ 'NPFX' => '0:1',
+ 'GIVN' => '0:1',
+ 'SPFX' => '0:1',
+ 'SURN' => '0:1',
+ 'NSFX' => '0:1',
+ 'NICK' => '0:1',
+ ];
}
diff --git a/app/Elements/NameRomanizedVariation.php b/app/Elements/NameRomanizedVariation.php
index ffec3581ff..85764f4458 100644
--- a/app/Elements/NameRomanizedVariation.php
+++ b/app/Elements/NameRomanizedVariation.php
@@ -35,10 +35,18 @@ use function view;
*/
class NameRomanizedVariation extends AbstractElement
{
- protected const SUBTAGS = ['NPFX', 'GIVN', 'SPFX','SURN', 'NSFX', 'NICK', 'TYPE'];
-
protected const MAXIMUM_LENGTH = 120;
+ protected const SUBTAGS = [
+ 'TYPE' => '0:1',
+ 'NPFX' => '0:1',
+ 'GIVN' => '0:1',
+ 'SPFX' => '0:1',
+ 'SURN' => '0:1',
+ 'NSFX' => '0:1',
+ 'NICK' => '0:1',
+ ];
+
/**
* An edit control for this data.
*
diff --git a/app/Elements/NoteRecord.php b/app/Elements/NoteRecord.php
index e9aec25d27..3ad36123a4 100644
--- a/app/Elements/NoteRecord.php
+++ b/app/Elements/NoteRecord.php
@@ -29,5 +29,6 @@ class NoteRecord extends AbstractElement
'REFN' => '0:1',
'RIN' => '0:1',
'SOUR' => '0:M',
+ 'CHAN' => '0:1',
];
}
diff --git a/app/Elements/RepositoryRecord.php b/app/Elements/RepositoryRecord.php
index 4f073ee648..051ec0acff 100644
--- a/app/Elements/RepositoryRecord.php
+++ b/app/Elements/RepositoryRecord.php
@@ -33,5 +33,6 @@ class RepositoryRecord extends AbstractElement
'NOTE' => '0:M',
'REFN' => '0:1',
'RIN' => '0:1',
+ 'CHAN' => '0:1',
];
}
diff --git a/app/Elements/SourceRecord.php b/app/Elements/SourceRecord.php
index b71c0ecd77..2512532a37 100644
--- a/app/Elements/SourceRecord.php
+++ b/app/Elements/SourceRecord.php
@@ -36,5 +36,6 @@ class SourceRecord extends AbstractElement
'DATA' => '0:1',
'REFN' => '0:1',
'RIN' => '0:1',
+ 'CHAN' => '0:1',
];
}
diff --git a/app/Elements/XrefRepository.php b/app/Elements/XrefRepository.php
index 026a0cf651..ce92a63f8c 100644
--- a/app/Elements/XrefRepository.php
+++ b/app/Elements/XrefRepository.php
@@ -36,7 +36,8 @@ use function view;
class XrefRepository extends AbstractXrefElement
{
protected const SUBTAGS = [
- 'CALN' => '0:1'
+ 'CALN' => '0:1',
+ 'NOTE' => '0:M',
];
/**
diff --git a/app/Factories/ElementFactory.php b/app/Factories/ElementFactory.php
index 5f7abbd16d..178c9b70d7 100644
--- a/app/Factories/ElementFactory.php
+++ b/app/Factories/ElementFactory.php
@@ -62,9 +62,6 @@ use Fisharebest\Webtrees\Elements\CopyrightSourceData;
use Fisharebest\Webtrees\Elements\CountOfChildren;
use Fisharebest\Webtrees\Elements\CountOfMarriages;
use Fisharebest\Webtrees\Elements\Cremation;
-use Fisharebest\Webtrees\Elements\CustomElement;
-use Fisharebest\Webtrees\Elements\CustomEvent;
-use Fisharebest\Webtrees\Elements\CustomFact;
use Fisharebest\Webtrees\Elements\DateLdsOrd;
use Fisharebest\Webtrees\Elements\DateValue;
use Fisharebest\Webtrees\Elements\Death;
@@ -81,18 +78,14 @@ use Fisharebest\Webtrees\Elements\EventOrFactClassification;
use Fisharebest\Webtrees\Elements\EventsRecorded;
use Fisharebest\Webtrees\Elements\EventTypeCitedFrom;
use Fisharebest\Webtrees\Elements\FamilyRecord;
-use Fisharebest\Webtrees\Elements\FamilySearchFamilyTreeId;
-use Fisharebest\Webtrees\Elements\FamilyStatusText;
use Fisharebest\Webtrees\Elements\FileName;
use Fisharebest\Webtrees\Elements\FirstCommunion;
use Fisharebest\Webtrees\Elements\Form;
use Fisharebest\Webtrees\Elements\GedcomElement;
use Fisharebest\Webtrees\Elements\GenerationsOfAncestors;
use Fisharebest\Webtrees\Elements\GenerationsOfDescendants;
-use Fisharebest\Webtrees\Elements\GovIdentifier;
use Fisharebest\Webtrees\Elements\Graduation;
use Fisharebest\Webtrees\Elements\HeaderRecord;
-use Fisharebest\Webtrees\Elements\HierarchicalRelationship;
use Fisharebest\Webtrees\Elements\Immigration;
use Fisharebest\Webtrees\Elements\IndividualRecord;
use Fisharebest\Webtrees\Elements\LanguageId;
@@ -105,8 +98,6 @@ use Fisharebest\Webtrees\Elements\LdsEndowment;
use Fisharebest\Webtrees\Elements\LdsEndowmentDateStatus;
use Fisharebest\Webtrees\Elements\LdsSpouseSealing;
use Fisharebest\Webtrees\Elements\LdsSpouseSealingDateStatus;
-use Fisharebest\Webtrees\Elements\LocationRecord;
-use Fisharebest\Webtrees\Elements\MaidenheadLocator;
use Fisharebest\Webtrees\Elements\Marriage;
use Fisharebest\Webtrees\Elements\MarriageBanns;
use Fisharebest\Webtrees\Elements\MarriageContract;
@@ -140,7 +131,6 @@ use Fisharebest\Webtrees\Elements\NoteStructure;
use Fisharebest\Webtrees\Elements\Occupation;
use Fisharebest\Webtrees\Elements\OrdinanceProcessFlag;
use Fisharebest\Webtrees\Elements\Ordination;
-use Fisharebest\Webtrees\Elements\PafUid;
use Fisharebest\Webtrees\Elements\PedigreeLinkageType;
use Fisharebest\Webtrees\Elements\PermanentRecordFileNumber;
use Fisharebest\Webtrees\Elements\PhoneNumber;
@@ -160,10 +150,6 @@ use Fisharebest\Webtrees\Elements\ReceivingSystemName;
use Fisharebest\Webtrees\Elements\RelationIsDescriptor;
use Fisharebest\Webtrees\Elements\ReligiousAffiliation;
use Fisharebest\Webtrees\Elements\RepositoryRecord;
-use Fisharebest\Webtrees\Elements\ResearchTask;
-use Fisharebest\Webtrees\Elements\ResearchTaskPriority;
-use Fisharebest\Webtrees\Elements\ResearchTaskStatus;
-use Fisharebest\Webtrees\Elements\ResearchTaskType;
use Fisharebest\Webtrees\Elements\Residence;
use Fisharebest\Webtrees\Elements\ResponsibleAgency;
use Fisharebest\Webtrees\Elements\RestrictionNotice;
@@ -172,7 +158,6 @@ use Fisharebest\Webtrees\Elements\RoleInEvent;
use Fisharebest\Webtrees\Elements\RomanizedType;
use Fisharebest\Webtrees\Elements\ScholasticAchievement;
use Fisharebest\Webtrees\Elements\SexValue;
-use Fisharebest\Webtrees\Elements\SexXValue;
use Fisharebest\Webtrees\Elements\SocialSecurityNumber;
use Fisharebest\Webtrees\Elements\SourceCallNumber;
use Fisharebest\Webtrees\Elements\SourceData;
@@ -195,13 +180,11 @@ use Fisharebest\Webtrees\Elements\UnknownElement;
use Fisharebest\Webtrees\Elements\UserReferenceNumber;
use Fisharebest\Webtrees\Elements\UserReferenceType;
use Fisharebest\Webtrees\Elements\VersionNumber;
-use Fisharebest\Webtrees\Elements\WebtreesUser;
use Fisharebest\Webtrees\Elements\WhereWithinSource;
use Fisharebest\Webtrees\Elements\Will;
use Fisharebest\Webtrees\Elements\XrefAssociate;
use Fisharebest\Webtrees\Elements\XrefFamily;
use Fisharebest\Webtrees\Elements\XrefIndividual;
-use Fisharebest\Webtrees\Elements\XrefLocation;
use Fisharebest\Webtrees\Elements\XrefMedia;
use Fisharebest\Webtrees\Elements\XrefRepository;
use Fisharebest\Webtrees\Elements\XrefSource;
@@ -243,698 +226,7 @@ class ElementFactory implements ElementFactoryInterface
private function elements(): array
{
if ($this->elements === []) {
- // Gedcom 5.5.1
$this->elements = $this->gedcom551();
-
- // Aldfaer extensions
- $this->register([
- 'FAM:MARR_CIVIL' => new CustomEvent(I18N::translate('Civil marriage')),
- 'FAM:MARR_RELIGIOUS' => new CustomEvent(I18N::translate('Religious marriage')),
- 'FAM:MARR_PARTNERS' => new CustomEvent(I18N::translate('Registered partnership')),
- 'FAM:MARR_UNKNOWN' => new CustomEvent(I18N::translate('Marriage type unknown')),
- ]);
-
- // Ancestry extensions
- $this->register([
- 'INDI:*:SOUR:_APID' => new CustomElement(I18N::translate('Ancestry PID')),
- 'INDI:_EMPLOY' => new CustomEvent(I18N::translate('Occupation')),
- ]);
-
- // Brother’s Keeper extensions
- $this->register([
- 'FAM:*:_EVN' => new CustomElement('Event number'),
- 'FAM:CHIL:_FREL' => new CustomElement('Relationship to father'),
- 'FAM:CHIL:_MREL' => new CustomElement('Relationship to mother'),
- 'FAM:_COML' => new CustomEvent(I18N::translate('Common law marriage')),
- 'FAM:_MARI' => new CustomEvent(I18N::translate('Marriage intention')),
- 'FAM:_MBON' => new CustomEvent(I18N::translate('Marriage bond')),
- 'FAM:_NMR' => new CustomEvent(I18N::translate('Not married'), ['NOTE' => '0:M', 'SOUR' => '0:M']),
- 'FAM:_PRMN' => new CustomElement(I18N::translate('Permanent number')),
- 'FAM:_SEPR' => new CustomEvent(I18N::translate('Separated')),
- 'FAM:_TODO' => new CustomElement(I18N::translate('Research task')),
- 'INDI:*:_EVN' => new CustomElement('Event number'),
- 'INDI:NAME:_ADPN' => new NamePersonal(I18N::translate('Adopted name'), []),
- 'INDI:NAME:_AKAN' => new NamePersonal(I18N::translate('Also known as'), []),
- 'INDI:NAME:_BIRN' => new NamePersonal(I18N::translate('Birth name'), []),
- 'INDI:NAME:_CALL' => new NamePersonal('Called name', []),
- 'INDI:NAME:_CENN' => new NamePersonal('Census name', []),
- 'INDI:NAME:_CURN' => new NamePersonal('Current name', []),
- 'INDI:NAME:_FARN' => new NamePersonal(I18N::translate('Estate name'), []),
- 'INDI:NAME:_FKAN' => new NamePersonal('Formal name', []),
- 'INDI:NAME:_FRKA' => new NamePersonal('Formerly known as', []),
- 'INDI:NAME:_GERN' => new NamePersonal('German name', []),
- 'INDI:NAME:_HEBN' => new NamePersonal(I18N::translate('Hebrew name'), []),
- 'INDI:NAME:_HNM' => new NamePersonal(I18N::translate('Hebrew name'), []),
- 'INDI:NAME:_INDG' => new NamePersonal('Indigenous name', []),
- 'INDI:NAME:_INDN' => new NamePersonal('Indian name', []),
- 'INDI:NAME:_LNCH' => new NamePersonal('Legal name change', []),
- 'INDI:NAME:_MARN' => new NamePersonal('Married name', []),
- 'INDI:NAME:_MARNM' => new NamePersonal('Married name', []),
- 'INDI:NAME:_OTHN' => new NamePersonal('Other name', []),
- 'INDI:NAME:_RELN' => new NamePersonal('Religious name', []),
- 'INDI:NAME:_SHON' => new NamePersonal('Short name', []),
- 'INDI:NAME:_SLDN' => new NamePersonal('Soldier name', []),
- 'INDI:_ADPF' => new CustomElement(I18N::translate('Adopted by father')),
- 'INDI:_ADPM' => new CustomElement(I18N::translate('Adopted by mother')),
- 'INDI:_BRTM' => new CustomEvent(I18N::translate('Brit milah')),
- 'INDI:_BRTM:DATE' => new DateValue(I18N::translate('Date of brit milah')),
- 'INDI:_BRTM:PLAC' => new PlaceName(I18N::translate('Place of brit milah')),
- 'INDI:_EMAIL' => new AddressEmail(I18N::translate('Email address')),
- 'INDI:_EYEC' => new CustomFact(I18N::translate('Eye color')),
- 'INDI:_FRNL' => new CustomElement(I18N::translate('Funeral')),
- 'INDI:_HAIR' => new CustomFact(I18N::translate('Hair color')),
- 'INDI:_HEIG' => new CustomFact(I18N::translate('Height')),
- 'INDI:_INTE' => new CustomElement(I18N::translate('Interment')),
- 'INDI:_MEDC' => new CustomFact(I18N::translate('Medical')),
- 'INDI:_MILT' => new CustomElement(I18N::translate('Military service')),
- 'INDI:_NLIV' => new CustomFact(I18N::translate('Not living')),
- 'INDI:_NMAR' => new CustomEvent(I18N::translate('Never married'), ['NOTE' => '0:M', 'SOUR' => '0:M']),
- 'INDI:_PRMN' => new CustomElement(I18N::translate('Permanent number')),
- 'INDI:_TODO' => new CustomElement(I18N::translate('Research task')),
- 'INDI:_WEIG' => new CustomFact(I18N::translate('Weight')),
- 'INDI:_YART' => new CustomEvent(I18N::translate('Yahrzeit')),
- // 1 XXXX
- // 2 _EVN ##
- // 1 ASSO @Xnnn@
- // 2 RELA Witness at event _EVN ##
- ]);
-
- // familysearch.org extensions
- $this->register([
- 'INDI:_FSFTID' => /* I18N: familysearch.org */ new FamilySearchFamilyTreeId(I18N::translate('FamilySearch ID')),
- ]);
-
- // Family Tree Builder extensions
- $this->register([
- '*:_UPD' => new CustomElement(I18N::translate('Last change')), // e.g. "1 _UPD 14 APR 2012 00:14:10 GMT-5"
- 'INDI:NAME:_AKA' => new NamePersonal(I18N::translate('Also known as'), []),
- 'OBJE:_ALBUM' => new CustomElement(I18N::translate('Album')), // XREF to an album
- 'OBJE:_DATE' => new DateValue(I18N::translate('Date')),
- 'OBJE:_FILESIZE' => new CustomElement(I18N::translate('File size')),
- 'OBJE:_PHOTO_RIN' => new CustomElement(I18N::translate('Photo')),
- 'OBJE:_PLACE' => new PlaceName(I18N::translate('Place')),
- '_ALBUM:_PHOTO' => new CustomElement(I18N::translate('Photo')),
- '_ALBUM:_PHOTO:_PRIN' => new CustomElement(I18N::translate('Highlighted image')),
- ]);
-
- // Family Tree Maker extensions
- $this->register([
- 'FAM:CHIL:_FREL' => new CustomElement(I18N::translate('Relationship to father')),
- 'FAM:CHIL:_MREL' => new CustomElement(I18N::translate('Relationship to mother')),
- 'FAM:_DETS' => new CustomElement(I18N::translate('Death of one spouse')),
- 'FAM:_FA1' => new CustomElement('Fact 1'),
- 'FAM:_FA10' => new CustomElement('Fact 10'),
- 'FAM:_FA11' => new CustomElement('Fact 11'),
- 'FAM:_FA12' => new CustomElement('Fact 12'),
- 'FAM:_FA13' => new CustomElement('Fact 13'),
- 'FAM:_FA2' => new CustomElement('Fact 2'),
- 'FAM:_FA3' => new CustomElement('Fact 3'),
- 'FAM:_FA4' => new CustomElement('Fact 4'),
- 'FAM:_FA5' => new CustomElement('Fact 5'),
- 'FAM:_FA6' => new CustomElement('Fact 6'),
- 'FAM:_FA7' => new CustomElement('Fact 7'),
- 'FAM:_FA8' => new CustomElement('Fact 8'),
- 'FAM:_FA9' => new CustomElement('Fact 9'),
- 'FAM:_MEND' => new CustomElement(I18N::translate('Marriage ending status')),
- 'FAM:_MSTAT' => new CustomElement(I18N::translate('Marriage beginning status')),
- 'FAM:_SEPR' => new CustomElement(I18N::translate('Separation')),
- 'HEAD:_SCHEMA' => new CustomElement('Schema'),
- 'HEAD:_SCHEMA:FAM' => new CustomElement(I18N::translate('Family')),
- 'HEAD:_SCHEMA:FAM:_FA*:LABL' => new CustomElement(I18N::translate('Label')),
- 'HEAD:_SCHEMA:FAM:_FA1' => new CustomElement(I18N::translate('Fact 1')),
- 'HEAD:_SCHEMA:FAM:_FA10' => new CustomElement(I18N::translate('Fact 10')),
- 'HEAD:_SCHEMA:FAM:_FA11' => new CustomElement(I18N::translate('Fact 11')),
- 'HEAD:_SCHEMA:FAM:_FA12' => new CustomElement(I18N::translate('Fact 12')),
- 'HEAD:_SCHEMA:FAM:_FA13' => new CustomElement(I18N::translate('Fact 13')),
- 'HEAD:_SCHEMA:FAM:_FA2' => new CustomElement(I18N::translate('Fact 2')),
- 'HEAD:_SCHEMA:FAM:_FA3' => new CustomElement(I18N::translate('Fact 3')),
- 'HEAD:_SCHEMA:FAM:_FA4' => new CustomElement(I18N::translate('Fact 4')),
- 'HEAD:_SCHEMA:FAM:_FA5' => new CustomElement(I18N::translate('Fact 5')),
- 'HEAD:_SCHEMA:FAM:_FA6' => new CustomElement(I18N::translate('Fact 6')),
- 'HEAD:_SCHEMA:FAM:_FA7' => new CustomElement(I18N::translate('Fact 7')),
- 'HEAD:_SCHEMA:FAM:_FA8' => new CustomElement(I18N::translate('Fact 8')),
- 'HEAD:_SCHEMA:FAM:_FA9' => new CustomElement(I18N::translate('Fact 9')),
- 'HEAD:_SCHEMA:FAM:_M*:LABL' => new CustomElement(I18N::translate('Label')),
- 'HEAD:_SCHEMA:FAM:_MEND' => new CustomElement(I18N::translate('Marriage ending status')),
- 'HEAD:_SCHEMA:FAM:_MSTAT' => new CustomElement(I18N::translate('Marriage beginning status')),
- 'HEAD:_SCHEMA:INDI' => new CustomElement(I18N::translate('Individual')),
- 'HEAD:_SCHEMA:INDI:_FA*:LABL' => new CustomElement(I18N::translate('Label')),
- 'HEAD:_SCHEMA:INDI:_FA1' => new CustomElement(I18N::translate('Fact 1')),
- 'HEAD:_SCHEMA:INDI:_FA10' => new CustomElement(I18N::translate('Fact 10')),
- 'HEAD:_SCHEMA:INDI:_FA11' => new CustomElement(I18N::translate('Fact 11')),
- 'HEAD:_SCHEMA:INDI:_FA12' => new CustomElement(I18N::translate('Fact 12')),
- 'HEAD:_SCHEMA:INDI:_FA13' => new CustomElement(I18N::translate('Fact 13')),
- 'HEAD:_SCHEMA:INDI:_FA2' => new CustomElement(I18N::translate('Fact 2')),
- 'HEAD:_SCHEMA:INDI:_FA3' => new CustomElement(I18N::translate('Fact 3')),
- 'HEAD:_SCHEMA:INDI:_FA4' => new CustomElement(I18N::translate('Fact 4')),
- 'HEAD:_SCHEMA:INDI:_FA5' => new CustomElement(I18N::translate('Fact 5')),
- 'HEAD:_SCHEMA:INDI:_FA6' => new CustomElement(I18N::translate('Fact 6')),
- 'HEAD:_SCHEMA:INDI:_FA7' => new CustomElement(I18N::translate('Fact 7')),
- 'HEAD:_SCHEMA:INDI:_FA8' => new CustomElement(I18N::translate('Fact 8')),
- 'HEAD:_SCHEMA:INDI:_FA9' => new CustomElement(I18N::translate('Fact 9')),
- 'HEAD:_SCHEMA:INDI:_FREL' => new CustomElement('Relationship to father'),
- 'HEAD:_SCHEMA:INDI:_M*:LABL' => new CustomElement(I18N::translate('Label')),
- 'HEAD:_SCHEMA:INDI:_MREL' => new CustomElement('Relationship to mother'),
- 'INDI:*:SOUR:_APID' => new CustomElement('Ancestry.com source identifier'),
- 'INDI:*:SOUR:_LINK' => new CustomElement('External link'),
- 'INDI:NAME:_AKA' => new NamePersonal(I18N::translate('Also known as'), []),
- 'INDI:NAME:_MARNM' => new NamePersonal(I18N::translate('Married name'), []),
- 'INDI:_CIRC' => new CustomElement('Circumcision'),
- 'INDI:_DCAUSE' => new CustomElement(I18N::translate('Cause of death')),
- 'INDI:_DEG' => new CustomElement(I18N::translate('Degree')),
- 'INDI:_DNA' => new CustomElement(I18N::translate('DNA markers')),
- 'INDI:_ELEC' => new CustomElement('Elected'),
- 'INDI:_EMPLOY' => new CustomElement('Employment'),
- 'INDI:_EXCM' => new CustomElement('Excommunicated'),
- 'INDI:_FA1' => new CustomElement('Fact 1'),
- 'INDI:_FA10' => new CustomElement('Fact 10'),
- 'INDI:_FA11' => new CustomElement('Fact 11'),
- 'INDI:_FA12' => new CustomElement('Fact 12'),
- 'INDI:_FA13' => new CustomElement('Fact 13'),
- 'INDI:_FA2' => new CustomElement('Fact 2'),
- 'INDI:_FA3' => new CustomElement('Fact 3'),
- 'INDI:_FA4' => new CustomElement('Fact 4'),
- 'INDI:_FA5' => new CustomElement('Fact 5'),
- 'INDI:_FA6' => new CustomElement('Fact 6'),
- 'INDI:_FA7' => new CustomElement('Fact 7'),
- 'INDI:_FA8' => new CustomElement('Fact 8'),
- 'INDI:_FA9' => new CustomElement('Fact 9'),
- 'INDI:_MDCL' => new CustomElement('Medical'),
- 'INDI:_MILT' => new CustomElement(I18N::translate('Military service')),
- 'INDI:_MILTID' => new CustomElement('Military ID number'),
- 'INDI:_MISN' => new CustomElement('Mission'),
- 'INDI:_NAMS' => new CustomElement(I18N::translate('Namesake')),
- 'INDI:_UNKN' => new CustomElement(I18N::translate('Unknown')), // Special individual ID code for later file comparisons
- // The context and meaning of these tags is unknown
- '_FOOT' => new CustomElement(''),
- '_FUN' => new CustomElement(''),
- '_JUST' => new CustomElement(''),
- '_PHOTO' => new CustomElement(''),
- ]);
-
- // Gedcom 5.3 extensions
- $this->register([
- 'EVEN' => new CustomElement('Event'),
- 'EVEN:*:*:NAME' => new CustomElement(I18N::translate('Name')),
- 'EVEN:*:AUDIO' => new CustomElement(I18N::translate('Audio')),
- 'EVEN:*:BROT' => new PlaceName('Brother'),
- 'EVEN:*:BUYR' => new PlaceName('Buyer'),
- 'EVEN:*:CHIL' => new PlaceName('Child'),
- 'EVEN:*:DATE' => new DateValue('Date'),
- 'EVEN:*:FATH' => new PlaceName('Father'),
- 'EVEN:*:GODP' => new PlaceName('Godparent'),
- 'EVEN:*:HDOH' => new PlaceName('Head of household'),
- 'EVEN:*:HEIR' => new PlaceName('Heir'),
- 'EVEN:*:HFAT' => new PlaceName('Husband’s father'),
- 'EVEN:*:HMOT' => new PlaceName('Husband’s mother'),
- 'EVEN:*:HUSB' => new PlaceName('Husband'),
- 'EVEN:*:IMAGE' => new CustomElement('Image'),
- 'EVEN:*:INDI' => new PlaceName('Individual'),
- 'EVEN:*:INFT' => new PlaceName('Informant'),
- 'EVEN:*:LEGA' => new PlaceName('Legatee'),
- 'EVEN:*:MBR' => new PlaceName('Member'),
- 'EVEN:*:MOTH' => new PlaceName('Mother'),
- 'EVEN:*:OFFI' => new PlaceName('Official'),
- 'EVEN:*:PARE' => new PlaceName('Parent'),
- 'EVEN:*:PHOTO' => new CustomElement(I18N::translate('Photo')),
- 'EVEN:*:PHUS' => new PlaceName('Previous husband'),
- 'EVEN:*:PLAC' => new PlaceName('Place'),
- 'EVEN:*:PWIF' => new PlaceName('Previous wife'),
- 'EVEN:*:RECO' => new PlaceName('Recorder'),
- 'EVEN:*:REL' => new PlaceName('Relative'),
- 'EVEN:*:SELR' => new PlaceName('Seller'),
- 'EVEN:*:SIBL' => new PlaceName('Sibling'),
- 'EVEN:*:SIST' => new PlaceName('Sister'),
- 'EVEN:*:SPOU' => new PlaceName('Spouse'),
- 'EVEN:*:TXPY' => new PlaceName('Taxpayer'),
- 'EVEN:*:VIDEO' => new CustomElement(I18N::translate('Video')),
- 'EVEN:*:WFAT' => new PlaceName('Wife’s father'),
- 'EVEN:*:WIFE' => new PlaceName('Wife'),
- 'EVEN:*:WITN' => new PlaceName('Witness'),
- 'EVEN:*:WMOT' => new PlaceName('Wife’s mother'),
- 'EVEN:TYPE' => new CustomElement('Type of event'),
- 'FAM:*:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'FAM:*:PLAC:SITE' => new CustomElement('Site'),
- 'FAM:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'FAM:AUDIO' => new CustomElement(I18N::translate('Audio')),
- 'FAM:IMAGE' => new CustomElement('Image'),
- 'FAM:PHOTO' => new CustomElement(I18N::translate('Photo')),
- 'FAM:VIDEO' => new CustomElement(I18N::translate('Video')),
- 'HEAD:SCHEMA' => new CustomElement(I18N::translate('Unknown')),
- 'HEAD:SCHEMA:FAM' => new CustomElement(I18N::translate('Family')),
- 'HEAD:SCHEMA:FAM:*:_*' => new CustomElement('Custom event'),
- 'HEAD:SCHEMA:FAM:*:_*:DEFN' => new CustomElement('Definition'),
- 'HEAD:SCHEMA:FAM:*:_*:ISA' => new CustomElement('Type of event'),
- 'HEAD:SCHEMA:FAM:*:_*:LABL' => new CustomElement('Label'),
- 'HEAD:SCHEMA:FAM:_*' => new CustomElement('Custom event'),
- 'HEAD:SCHEMA:FAM:_*:DEFN' => new CustomElement('Definition'),
- 'HEAD:SCHEMA:FAM:_*:ISA' => new CustomElement('Type of event'),
- 'HEAD:SCHEMA:FAM:_*:LABL' => new CustomElement('Label'),
- 'HEAD:SCHEMA:INDI' => new CustomElement(I18N::translate('Individual')),
- 'HEAD:SCHEMA:INDI:*:_*' => new CustomElement('Custom event'),
- 'HEAD:SCHEMA:INDI:*:_*:DEFN' => new CustomElement('Definition'),
- 'HEAD:SCHEMA:INDI:*:_*:ISA' => new CustomElement('Type of event'),
- 'HEAD:SCHEMA:INDI:*:_*:LABL' => new CustomElement('Label'),
- 'HEAD:SCHEMA:INDI:_*' => new CustomElement('Custom event'),
- 'HEAD:SCHEMA:INDI:_*:DEFN' => new CustomElement('Definition'),
- 'HEAD:SCHEMA:INDI:_*:ISA' => new CustomElement('Type of event'),
- 'HEAD:SCHEMA:INDI:_*:LABL' => new CustomElement('Label'),
- 'INDI:*:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'INDI:*:PLAC:SITE' => new CustomElement('Site'),
- 'INDI:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'INDI:AUDIO' => new CustomElement(I18N::translate('Audio')),
- 'INDI:BURI:PLAC:CEME' => new CustomElement(I18N::translate('Cemetery')),
- 'INDI:BURI:PLAC:CEME:PLOT' => new CustomElement('Burial plot'),
- 'INDI:IMAGE' => new CustomElement('Image'),
- 'INDI:NAMR' => new CustomElement(I18N::translate('Religious name')),
- 'INDI:NAMS' => new CustomElement(I18N::translate('Namesake')),
- 'INDI:PHOTO' => new CustomElement(I18N::translate('Photo')),
- 'INDI:SIGN' => new CustomElement('Signature'),
- 'INDI:VIDEO' => new CustomElement(I18N::translate('Video')),
- 'REPO:CALN:ITEM' => new CustomElement('Item'),
- 'REPO:CALN:PAGE' => new CustomElement('Page'),
- 'REPO:CALN:SHEE' => new CustomElement('Sheet'),
- 'REPO:CNTC' => new CustomElement('Contact person'),
- 'REPO:MEDI' => new SourceMediaType(I18N::translate('Media type')),
- 'REPO:REFN' => new CustomElement('Reference number'),
- 'SOUR:AUDIO' => new CustomElement(I18N::translate('Audio')),
- 'SOUR:CENS' => new CustomElement('Census'),
- 'SOUR:CENS:DATE' => new CustomElement('Census'),
- 'SOUR:CENS:DWEL' => new CustomElement('Dwelling number'),
- 'SOUR:CENS:FAMN' => new CustomElement('Family number'),
- 'SOUR:CENS:LINE' => new CustomElement('Line number'),
- 'SOUR:CLAS' => new CustomElement('Source classification'),
- 'SOUR:CPLR' => new CustomElement('Compiler'),
- 'SOUR:EDTR' => new CustomElement('Editor'),
- 'SOUR:EVEN' => new CustomElement('Source events'),
- 'SOUR:FIDE' => new CustomElement('Fidelity'),
- 'SOUR:FILM' => new CustomElement(I18N::translate('Microfilm')),
- 'SOUR:IMAGE' => new CustomElement('Image'),
- 'SOUR:INDX' => new CustomElement('Indexed'),
- 'SOUR:INTV' => new CustomElement('Interviewer'),
- 'SOUR:ORIG' => new CustomElement('Originator'),
- 'SOUR:ORIG:NAME' => new CustomElement('Name'),
- 'SOUR:ORIG:NOTE' => new CustomElement('Note'),
- 'SOUR:ORIG:TYPE' => new CustomElement('Type'),
- 'SOUR:PERI' => new CustomElement('Date period'),
- 'SOUR:PHOTO' => new CustomElement(I18N::translate('Photo')),
- 'SOUR:PUBL:DATE' => new CustomElement('Date'),
- 'SOUR:PUBL:EDTN' => new CustomElement('Edition'),
- 'SOUR:PUBL:ISSU' => new CustomElement('Issue'),
- 'SOUR:PUBL:LCCN' => new CustomElement('Library of Congress call number'),
- 'SOUR:PUBL:NAME' => new CustomElement('Name'),
- 'SOUR:PUBL:PUBR' => new CustomElement('Publisher'),
- 'SOUR:PUBL:SERS' => new CustomElement('Series'),
- 'SOUR:PUBL:TYPE' => new CustomElement('Type'),
- 'SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'SOUR:RECO' => new CustomElement('Recording agency?'),
- 'SOUR:REFS' => new XrefSource('Referenced source'),
- 'SOUR:REPO:DPRT:ARVL' => new CustomElement('Departure'),
- 'SOUR:REPO:DPRT:ARVL:DATE' => new DateValue('Date'),
- 'SOUR:REPO:DPRT:ARVL:PLAC' => new PlaceName('Place'),
- 'SOUR:REPO:NAME' => new CustomElement('Name of vessel'),
- 'SOUR:REPO:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'SOUR:REPO:PORT' => new CustomElement('Port'),
- 'SOUR:REPO:PORT:ARVL' => new CustomElement('Arrival'),
- 'SOUR:REPO:PORT:ARVL:DATE' => new DateValue('Date'),
- 'SOUR:REPO:PORT:ARVL:PLAC' => new PlaceName('Place'),
- 'SOUR:REPO:TEXT' => new TextFromSource(I18N::translate('Text')),
- 'SOUR:SEQU' => new CustomElement('Sequence'),
- 'SOUR:STAT' => new CustomElement('Search status'),
- 'SOUR:STAT:DATE' => new DateValue('Date'),
- 'SOUR:TEXT' => new TextFromSource(I18N::translate('Text')),
- 'SOUR:TYPE' => new CustomElement('Type of source'),
- 'SOUR:VIDEO' => new CustomElement(I18N::translate('Video')),
- 'SOUR:XLTR' => new CustomElement('Translator'),
- ]);
-
- // Gedcom 5.5 extensions
- $this->register([
- 'OBJE:BLOB' => new UnknownElement(I18N::translate('Binary data object')),
- ]);
-
- // Gedcom-L extensions
- $this->register([
- 'FAM:*:ADDR:_NAME' => new CustomElement('Name of addressee'),
- 'FAM:*:PLAC:_GOV' => new GovIdentifier(I18N::translate('GOV identifier')),
- 'FAM:*:PLAC:_LOC' => new XrefLocation(I18N::translate('Location')),
- 'FAM:*:PLAC:_MAIDENHEAD' => new MaidenheadLocator('Maidenhead locator'),
- 'FAM:*:PLAC:_POST' => new AddressPostalCode('Postal code'),
- 'FAM:*:PLAC:_POST:DATE' => new DateValue(I18N::translate('Date')),
- 'FAM:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
- 'FAM:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'FAM:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
- 'FAM:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
- 'FAM:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
- 'FAM:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
- 'FAM:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
- 'FAM:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
- 'FAM:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
- 'FAM:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'FAM:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
- 'FAM:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
- 'FAM:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'FAM:*:_WITN' => new CustomElement('Witness'),
- 'FAM:_STAT' => new FamilyStatusText(I18N::translate('Family status'), ['DATE' => '0:1', 'PLAC' => '0:1', 'NOTE' => '0:M', 'SOUR' => '0:M']),
- 'FAM:_TODO' => new ResearchTask(I18N::translate('Research task'), ['DESC' => '1:1', '_CAT' => '0:1', '_PRTY' => '0:1', 'TYPE' => '0:1', 'NOTE' => '0:M', 'DATA' => '0:1', 'STAT' => '0:1', '_CDATE' => '0:1', '_RDATE' => '0:1', 'REPO' => '0:1', '_UID' => '0:M']),
- 'FAM:_TODO:DATA' => new SubmitterText(I18N::translate('The solution')),
- 'FAM:_TODO:DATE' => new DateValue(I18N::translate('Creation date')),
- 'FAM:_TODO:DESC' => new CustomElement(I18N::translate('Description')),
- 'FAM:_TODO:NOTE' => new SubmitterText(I18N::translate('The problem')),
- 'FAM:_TODO:REPO' => new XrefRepository('Repository', []),
- 'FAM:_TODO:STAT' => new ResearchTaskStatus(I18N::translate('Status')),
- 'FAM:_TODO:TYPE' => new ResearchTaskType(I18N::translate('Type of research task')),
- 'FAM:_TODO:_CAT' => new CustomElement(I18N::translate('Category')),
- 'FAM:_TODO:_CDATE' => new DateValue(I18N::translate('Completion date')),
- 'FAM:_TODO:_PRTY' => new ResearchTaskPriority(I18N::translate('Priority')),
- 'FAM:_TODO:_RDATE' => new DateValue(I18N::translate('Reminder date')),
- 'FAM:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'HEAD:SOUR:CORP:ADDR:_NAME' => new CustomElement('Name of addressee'),
- 'HEAD:_SCHEMA' => new EmptyElement(I18N::translate('Schema')),
- 'HEAD:_SCHEMA:*' => new EmptyElement(I18N::translate('Base GEDCOM tag')),
- 'HEAD:_SCHEMA:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
- 'HEAD:_SCHEMA:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
- 'HEAD:_SCHEMA:*:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
- 'HEAD:_SCHEMA:*:*:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
- 'HEAD:_SCHEMA:*:*:*:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
- 'HEAD:_SCHEMA:*:*:*:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
- 'HEAD:_SCHEMA:*:*:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
- 'HEAD:_SCHEMA:*:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
- 'HEAD:_SCHEMA:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
- 'HEAD:_SCHEMA:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
- 'INDI:*:ADDR:_NAME' => new CustomElement('Name of addressee'),
- 'INDI:*:PLAC:_GOV' => new GovIdentifier(I18N::translate('GOV identifier')),
- 'INDI:*:PLAC:_LOC' => new XrefLocation(I18N::translate('Location')),
- 'INDI:*:PLAC:_MAIDENHEAD' => new MaidenheadLocator('Maidenhead locator'),
- 'INDI:*:PLAC:_POST' => new AddressPostalCode('Postal code'),
- 'INDI:*:PLAC:_POST:DATE' => new DateValue(I18N::translate('Date')),
- 'INDI:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
- 'INDI:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'INDI:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
- 'INDI:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
- 'INDI:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
- 'INDI:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
- 'INDI:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
- 'INDI:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
- 'INDI:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
- 'INDI:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'INDI:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
- 'INDI:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
- 'INDI:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'INDI:*:_WITN' => new CustomElement('Witness'),
- 'INDI:BAPM:_GODP' => new CustomElement('Godparent'),
- 'INDI:CHR:_GODP' => new CustomElement('Godparent'),
- 'INDI:NAME:_RUFNAME' => new NamePieceGiven(I18N::translate('Rufname')),
- 'INDI:OBJE:_PRIM' => new CustomElement(I18N::translate('Highlighted image')),
- 'INDI:SEX' => new SexXValue(I18N::translate('Gender')),
- 'INDI:_TODO' => new ResearchTask(I18N::translate('Research task')),
- 'INDI:_TODO:DATA' => new SubmitterText(I18N::translate('The solution')),
- 'INDI:_TODO:DATE' => new DateValue(I18N::translate('Creation date')),
- 'INDI:_TODO:DESC' => new CustomElement(I18N::translate('Description')),
- 'INDI:_TODO:NOTE' => new SubmitterText(I18N::translate('The problem')),
- 'INDI:_TODO:REPO' => new XrefRepository('Repository', []),
- 'INDI:_TODO:STAT' => new ResearchTaskStatus(I18N::translate('Status')),
- 'INDI:_TODO:TYPE' => new ResearchTaskType(I18N::translate('Type of research task')),
- 'INDI:_TODO:_CAT' => new CustomElement(I18N::translate('Category')),
- 'INDI:_TODO:_CDATE' => new DateValue(I18N::translate('Completion date')),
- 'INDI:_TODO:_PRTY' => new ResearchTaskPriority(I18N::translate('Priority')),
- 'INDI:_TODO:_RDATE' => new DateValue(I18N::translate('Reminder date')),
- 'INDI:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'NOTE:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'OBJE:FILE:_PRIM' => new CustomElement(I18N::translate('Highlighted image')),
- 'OBJE:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'REPO:ADDR:_NAME' => new CustomElement('Name of addressee'),
- 'REPO:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'SOUR:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'SUBM:ADDR:_NAME' => new CustomElement('Name of addressee'),
- 'SUBM:_UID' => new PafUid(I18N::translate('Unique identifier')),
- 'SUBN:_UID' => new PafUid(I18N::translate('Unique identifier')),
- '_LOC' => new LocationRecord(I18N::translate('Location')),
- '_LOC:CHAN' => new Change(I18N::translate('Last change')),
- '_LOC:CHAN:DATE' => new ChangeDate(I18N::translate('Date of last change')),
- '_LOC:CHAN:DATE:TIME' => new TimeValue(I18N::translate('Time')),
- '_LOC:CHAN:NOTE' => new NoteStructure(I18N::translate('Note')),
- '_LOC:EVEN' => new EventDescriptor(I18N::translate('Event'), ['TYPE' => '0:1']),
- '_LOC:EVEN:TYPE' => new EventAttributeType(I18N::translate('Type of event')),
- '_LOC:MAP' => new EmptyElement(I18N::translate('Coordinates')),
- '_LOC:MAP:LATI' => new PlaceLatitude(I18N::translate('Latitude')),
- '_LOC:MAP:LONG' => new PlaceLongtitude(I18N::translate('Longitude')),
- '_LOC:NAME' => new PlaceName(I18N::translate('Place'), ['ABBR' => '0:1', 'DATE' => '0:1', 'LANG' => '0:1', 'SOUR' => '0:M']),
- '_LOC:NAME:ABBR' => new CustomElement(I18N::translate('Abbreviation')),
- '_LOC:NAME:ABBR:TYPE' => new CustomElement(I18N::translate('Type of abbreviation')),
- '_LOC:NAME:DATE' => new DateValue(I18N::translate('Date')),
- '_LOC:NAME:LANG' => new LanguageId(I18N::translate('Language')),
- '_LOC:NAME:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:NOTE' => new NoteStructure(I18N::translate('Note')),
- '_LOC:OBJE' => new XrefMedia(I18N::translate('Media')),
- '_LOC:RELI' => new ReligiousAffiliation('Religion', []),
- '_LOC:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:SOUR:DATA' => new SourceData(I18N::translate('Data')),
- '_LOC:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
- '_LOC:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
- '_LOC:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
- '_LOC:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
- '_LOC:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
- '_LOC:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
- '_LOC:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
- '_LOC:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- '_LOC:TYPE' => new CustomElement(I18N::translate('Type of location')),
- '_LOC:TYPE:DATE' => new DateValue(I18N::translate('Date')),
- '_LOC:TYPE:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:TYPE:_GOVTYPE' => new CustomElement('GOV identifier type'),
- '_LOC:_AIDN' => new CustomElement('Administrative ID'),
- '_LOC:_AIDN:DATE' => new DateValue(I18N::translate('Date')),
- '_LOC:_AIDN:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:_AIDN:TYPE' => new CustomElement(I18N::translate('Type of administrative ID')),
- '_LOC:_DMGD' => new CustomElement('Demographic data'),
- '_LOC:_DMGD:DATE' => new DateValue(I18N::translate('Date')),
- '_LOC:_DMGD:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:_DMGD:TYPE' => new CustomElement(I18N::translate('Type of demographic data')),
- '_LOC:_GOV' => new GovIdentifier(I18N::translate('GOV identifier')),
- '_LOC:_LOC' => new XrefLocation(I18N::translate('Parent'), ['DATE' => '0:1', 'SOUR' => '0:M', 'TYPE' => '0:1']),
- '_LOC:_LOC:DATE' => new DateValue(I18N::translate('Date')),
- '_LOC:_LOC:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:_LOC:TYPE' => new HierarchicalRelationship(I18N::translate('Hierarchical relationship')),
- '_LOC:_MAIDENHEAD' => new MaidenheadLocator('Maidenhead locator'),
- '_LOC:_POST' => new AddressPostalCode(I18N::translate('Postal code')),
- '_LOC:_POST:DATE' => new DateValue(I18N::translate('Date')),
- '_LOC:_POST:SOUR' => new XrefSource(I18N::translate('Source')),
- '_LOC:_UID' => new PafUid(I18N::translate('Unique identifier')),
- '_LOC:*:SOUR:DATA' => new SourceData(I18N::translate('Data')),
- '_LOC:*:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
- '_LOC:*:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
- '_LOC:*:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
- '_LOC:*:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
- '_LOC:*:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
- '_LOC:*:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
- '_LOC:*:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
- '_LOC:*:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- ]);
-
- // Legacy extensions
- $this->register([
- 'FAM:*:ADDR:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
- 'FAM:*:PLAC:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
- 'FAM:*:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
- 'FAM:*:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
- 'FAM:CHIL:_FREL' => new CustomElement('The Relationship of a child to the Father (under a CHIL block under a FAM record).'),
- 'FAM:CHIL:_MREL' => new CustomElement('The Relationship of a child to the Mother (under a CHIL block under a FAM record).'),
- 'FAM:CHIL:_STAT' => new CustomElement('The Status of a marriage (Married, Unmarried, etc.). Also the Status of a child (Twin, Triplet, etc.). (The marriage status of Divorced is exported using a DIV tag.)'),
- 'FAM:EVEN:_OVER' => new CustomElement('An event sentence override (under an EVEN block).'),
- 'FAM:MARR:_STAT' => new CustomElement('The Status of a marriage (Married, Unmarried, etc.). Also the Status of a child (Twin, Triplet, etc.). (The marriage status of Divorced is exported using a DIV tag.)'),
- 'FAM:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
- 'FAM:_NONE' => new CustomElement('Indicates that a couple had no children (under a FAM record).'),
- 'HEAD:_EVENT_DEFN' => new CustomElement('Indicates the start of an Event Definition record that describes the attributes of an event or fact.'),
- 'HEAD:_EVENT_DEFN:_CONF_FLAG' => new CustomElement('Indicates that an event is Confidential or Private (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_DATE_TYPE' => new CustomElement('Indicates whether or not a Date field is shown for a specific event (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_DESC_FLAG' => new CustomElement('Indicates whether or not a Description field is shown for a specific event (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_PLACE_TYPE' => new CustomElement('Indicates whether or not a Place field is shown for a specific event (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_PP_EXCLUDE' => new CustomElement('Indicates that an event is to be Excluded from the Potential Problems reporting (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN1' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN2' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN3' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN4' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN5' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN6' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN7' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SEN8' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENDOF' => new CustomElement('Event sentence for PAF5 if only the Date field is filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENDOM' => new CustomElement('Event sentence for PAF5 if only the Date field is filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENDOU' => new CustomElement('Event sentence for PAF5 if only the Date field is filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENDPF' => new CustomElement('Event sentence for PAF5 if only the Date and Place fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENDPM' => new CustomElement('Event sentence for PAF5 if only the Date and Place fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENDPU' => new CustomElement('Event sentence for PAF5 if only the Date and Place fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENF' => new CustomElement('Event sentence for PAF5 if all fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENM' => new CustomElement('Event sentence for PAF5 if all fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENPOF' => new CustomElement('Event sentence for PAF5 if only the Place field is filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENPOM' => new CustomElement('Event sentence for PAF5 if only the Place field is filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENPOU' => new CustomElement('Event sentence for PAF5 if only the Place field is filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_EVENT_DEFN:_SENU' => new CustomElement('Event sentence for PAF5 if all fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
- 'HEAD:_PLAC_DEFN' => new CustomElement('Indicates the start of a Place Definition record that describes the attribute of a place.'),
- 'HEAD:_PLAC_DEFN:_PREP' => new CustomElement('A location Preposition (under a _PLAC_DEFN record).'),
- 'INDI:*:ADDR:_LIST3 YES' => new CustomElement('Indicates that a person’s address is part of the Birthday grouping (under an ADDR block).'),
- 'INDI:*:ADDR:_LIST4 YES' => new CustomElement('Indicates that a person’s address is part of the Research grouping (under an ADDR block).'),
- 'INDI:*:ADDR:_LIST5 YES' => new CustomElement('Indicates that a person’s address is part of the Christmas grouping (under an ADDR block).'),
- 'INDI:*:ADDR:_LIST6 YES' => new CustomElement('Indicates that a person’s address is part of the Holiday grouping (under an ADDR block).'),
- 'INDI:*:ADDR:_NAME' => new CustomElement('The name of an individual as part of an address (under an ADDR block).'),
- 'INDI:*:ADDR:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
- 'INDI:*:ADDR:_SORT' => new CustomElement('The spelling of a name to be used when sorting addresses for a report (under an ADDR block).'),
- 'INDI:*:ADDR:_TAG' => new CustomElement('Indicates that an address, or place has been tagged. Also used for Tag 1 selection for an individual.'),
- 'INDI:*:PLAC:_TAG' => new CustomElement('Indicates that an address, or place has been tagged. Also used for Tag 1 selection for an individual.'),
- 'INDI:*:PLAC:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
- 'INDI:*:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
- 'INDI:*:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
- 'INDI:ADDR:_EMAIL' => new CustomElement('An email address (under an ADDR block).'),
- 'INDI:ADDR:_LIST1 YES' => new CustomElement('Indicates that a person’s address is part of the Newsletter grouping (under an ADDR block).'),
- 'INDI:ADDR:_LIST2 YES' => new CustomElement('Indicates that a person’s address is part of the Family Association grouping (under an ADDR block).'),
- 'INDI:EVEN:_OVER' => new CustomElement('An event sentence override (under an EVEN block).'),
- 'INDI:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
- 'INDI:_TAG' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG2' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG3' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG4' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG5' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG6' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG7' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG8' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TAG9' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
- 'INDI:_TODO' => new CustomElement('Research task'),
- 'INDI:_TODO:_CAT' => new CustomElement('The Category of a To-Do item (under a _TODO record).'),
- 'INDI:_TODO:_CDATE' => new CustomElement('Closed Date of a To-Do item (under a _TODO record).'),
- 'INDI:_TODO:_LOCL' => new CustomElement('The Locality of a To-Do item (under a _TODO record).'),
- 'INDI:_TODO:_RDATE' => new CustomElement('Reminder date on to-do items. (Under a _TODO record.)'),
- 'INDI:_UID' => new CustomElement('A Unique Identification Number given to each individual in a family file.'),
- 'INDI:_URL' => new CustomElement('An Internet address (under an INDI record).'),
- 'OBJE:_DATE' => new CustomElement('A date associated with a multimedia object, usually a picture or video (under an OBJE block).'),
- 'OBJE:_PRIM' => new CustomElement('Means a multimedia object, usually a picture, is the Primary object (the one that is shown on a report) (under an OBJE block).'),
- 'OBJE:_SCBK' => new CustomElement('Indicates that a Picture is tagged to be included in a scrapbook report (under an OBJE block).'),
- 'OBJE:_SOUND' => new CustomElement('A sound file name that is attached to a picture (under an OBJE block).'),
- 'OBJE:_TYPE' => new CustomElement('The type of a multimedia object: Photo, Sound, or Video (under an OBJE block).'),
- 'SOUR:_ITALIC Y' => new CustomElement('Indicates that a source title should be printed on a report in italics (under a SOUR record).'),
- 'SOUR:_PAREN' => new CustomElement('Indicates that the Publication Facts of a source should be printed within parentheses on a report (under a SOUR record).'),
- 'SOUR:_QUOTED Y' => new CustomElement('Indicates that a source title should be printed within quotes on a report (under a SOUR record).'),
- 'SOUR:_TAG NO' => new CustomElement('When used under a SOUR record, indicates to exclude the source citation detail on reports.'),
- 'SOUR:_TAG2 NO' => new CustomElement('When used under a SOUR record, indicates to exclude the source citation on reports.'),
- 'SOUR:_TAG3 YES' => new CustomElement('When used under a SOUR record, indicates to include the source citation detail text on reports.'),
- 'SOUR:_TAG4 YES' => new CustomElement('When used under a SOUR record, indicates to include the source citation detail notes on reports.'),
- '_PREF' => new CustomElement('Indicates a Preferred spouse, child or parents.'), // How is this used?
- ]);
-
- // Personal Ancestral File extensions
- $this->register([
- 'INDI:NAME:_ADPN' => new NamePersonal(I18N::translate('Adopted name'), []),
- 'INDI:NAME:_AKA' => new NamePersonal(I18N::translate('Also known as'), []),
- 'INDI:NAME:_AKAN' => new NamePersonal(I18N::translate('Also known as'), []),
- 'INDI:_EMAIL' => new AddressEmail(I18N::translate('Email address')),
- 'URL' => new CustomElement(I18N::translate('URL')),
- '_HEB' => new CustomElement(I18N::translate('Hebrew')),
- '_NAME' => new CustomElement(I18N::translate('Mailing name')),
- '_SCBK' => new CustomElement(I18N::translate('Scrapbook')),
- '_SSHOW' => new CustomElement(I18N::translate('Slide show')),
- '_TYPE' => new CustomElement(I18N::translate('Media type')),
- '_URL' => new CustomElement(I18N::translate('URL')),
- ]);
-
- // PhpGedView extensions
- $this->register([
- 'FAM:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
- 'FAM:COMM' => new CustomElement(I18N::translate('Comment')),
- 'INDI:*:ASSO' => new XrefAssociate(I18N::translate('Associate')),
- 'INDI:*:ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
- 'INDI:*:PLAC:_HEB' => new NoteStructure(I18N::translate('Place in Hebrew')),
- 'INDI:ADDR' => new AddressLine(I18N::translate('Address'), []),
- 'INDI:BIRT:DATE:TIME' => new TimeValue(I18N::translate('Time')),
- 'INDI:BURI:CEME' => new CustomElement(I18N::translate('Cemetery')),
- 'INDI:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
- 'INDI:COMM' => new CustomElement(I18N::translate('Comment')),
- 'INDI:DEAT:DATE:TIME' => new TimeValue(I18N::translate('Time')),
- 'INDI:EMAIL' => new AddressEmail(I18N::translate('Email address')),
- 'INDI:NAME:_HEB' => new NamePersonal(I18N::translate('Name in Hebrew'), []),
- 'INDI:_FNRL' => new CustomEvent(I18N::translate('Funeral')),
- 'INDI:_HOL' => new CustomEvent(I18N::translate('Holocaust')),
- 'INDI:_MILI' => new CustomEvent(I18N::translate('Military')),
- 'INDI:_PGV_OBJS' => new XrefMedia(I18N::translate('Re-order media')),
- 'NOTE:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
- 'OBJE:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
- 'OBJE:_PRIM' => new CustomElement(I18N::translate('Highlighted image')),
- 'OBJE:_THUM' => new CustomElement(I18N::translate('Thumbnail image')),
- 'REPO:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
- 'SOUR:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
- 'SOUR:SERV' => new CustomElement(I18N::translate('Remote server')),
- 'SOUR:URL' => new AddressWebPage(I18N::translate('URL')),
- 'SOUR:URL:TYPE' => new CustomElement(I18N::translate('Type')), // e.g. "FamilySearch"
- 'SOUR:URL:_BLOCK' => new CustomElement(I18N::translate('Block')), // "e.g. "false"
- 'SOUR:_DBID' => new CustomElement(I18N::translate('Database name')),
- 'SOUR:_DBID:_PASS' => new CustomElement(I18N::translate('Database password')),
- 'SOUR:_DBID:_PASS:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- 'SOUR:_DBID:_USER' => new CustomElement(I18N::translate('Database user account')),
- ]);
-
- // Reunion extensions
- $this->register([
- 'INDI:EMAL' => new AddressEmail(I18N::translate('Email address')),
- 'INDI:CITN' => new CustomElement(I18N::translate('Citizenship')),
- 'INDI:_LEGA' => new CustomElement(I18N::translate('Legatee')),
- 'INDI:_MDCL' => new CustomElement(I18N::translate('Medical')),
- 'INDI:_PURC' => new CustomElement('Land purchase'),
- 'INDI:_SALE' => new CustomElement('Land sale'),
- ]);
-
- // Roots Magic extensions
- $this->register([
- 'INDI:_DNA' => new CustomElement(I18N::translate('DNA markers')),
- 'INDI:_WEBTAG' => new CustomElement(I18N::translate('External link')),
- 'INDI:_WEBTAG:NAME' => new CustomElement(I18N::translate('Text')),
- 'INDI:_WEBTAG:URL' => new AddressWebPage(I18N::translate('URL')),
- 'SOUR:_BIBL' => new CustomElement(I18N::translate('Bibliography')),
- 'SOUR:_SUBQ' => new CustomElement(I18N::translate('Abbreviation')),
- ]);
-
- // webtrees extensions
- $this->register([
- 'FAM:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'FAM:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
- 'FAM:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
- 'FAM:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
- 'FAM:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'FAM:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
- 'FAM:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
- 'FAM:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
- 'FAM:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
- 'FAM:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
- 'FAM:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
- 'FAM:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
- 'FAM:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'FAM:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
- 'FAM:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
- 'FAM:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'INDI:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'INDI:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
- 'INDI:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'INDI:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
- 'INDI:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
- 'INDI:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
- 'INDI:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
- 'INDI:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
- 'INDI:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
- 'INDI:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
- 'INDI:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
- 'INDI:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
- 'INDI:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
- 'INDI:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
- 'NOTE:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'NOTE:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- 'OBJE:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'OBJE:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- 'REPO:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'REPO:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- 'SOUR:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'SOUR:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- 'SUBM:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- 'SUBM:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- '_LOC:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
- '_LOC:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
- ]);
}
return $this->elements;
diff --git a/app/Http/RequestHandlers/ControlPanel.php b/app/Http/RequestHandlers/ControlPanel.php
index 6cde5e350a..4373ecc4ad 100644
--- a/app/Http/RequestHandlers/ControlPanel.php
+++ b/app/Http/RequestHandlers/ControlPanel.php
@@ -28,6 +28,7 @@ use Fisharebest\Webtrees\Module\ModuleAnalyticsInterface;
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleCustomInterface;
+use Fisharebest\Webtrees\Module\ModuleCustomTagsInterface;
use Fisharebest\Webtrees\Module\ModuleDataFixInterface;
use Fisharebest\Webtrees\Module\ModuleFooterInterface;
use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
@@ -178,6 +179,8 @@ class ControlPanel implements RequestHandlerInterface
'chart_modules_disabled' => $this->module_service->findByInterface(ModuleChartInterface::class, true),
'chart_modules_enabled' => $this->module_service->findByInterface(ModuleChartInterface::class),
'custom_updates' => $custom_updates,
+ 'custom_tags_modules_disabled' => $this->module_service->findByInterface(ModuleCustomTagsInterface::class, true),
+ 'custom_tags_modules_enabled' => $this->module_service->findByInterface(ModuleCustomTagsInterface::class),
'data_fix_modules_disabled' => $this->module_service->findByInterface(ModuleDataFixInterface::class, true),
'data_fix_modules_enabled' => $this->module_service->findByInterface(ModuleDataFixInterface::class),
'other_modules' => $this->module_service->otherModules(true),
diff --git a/app/Http/RequestHandlers/ModulesCustomTagsAction.php b/app/Http/RequestHandlers/ModulesCustomTagsAction.php
new file mode 100644
index 0000000000..41a03a3af6
--- /dev/null
+++ b/app/Http/RequestHandlers/ModulesCustomTagsAction.php
@@ -0,0 +1,50 @@
+<?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\FlashMessages;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Module\ModuleCustomTagsInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+
+use function redirect;
+use function route;
+
+/**
+ * Update a list of modules.
+ */
+class ModulesCustomTagsAction extends AbstractModuleComponentAction
+{
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $this->updateStatus(ModuleCustomTagsInterface::class, $request);
+ $this->updateAccessLevel(ModuleCustomTagsInterface::class, $request);
+
+ FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
+
+ return redirect(route(ModulesCustomTagsPage::class));
+ }
+}
diff --git a/app/Http/RequestHandlers/ModulesCustomTagsPage.php b/app/Http/RequestHandlers/ModulesCustomTagsPage.php
new file mode 100644
index 0000000000..051c3a506e
--- /dev/null
+++ b/app/Http/RequestHandlers/ModulesCustomTagsPage.php
@@ -0,0 +1,47 @@
+<?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\I18N;
+use Fisharebest\Webtrees\Module\ModuleCustomTagsInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+
+use function view;
+
+/**
+ * Show a list of modules.
+ */
+class ModulesCustomTagsPage extends AbstractModuleComponentPage
+{
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ return $this->listComponents(
+ ModuleCustomTagsInterface::class,
+ view('icons/chart') . I18N::translate('Custom tags'),
+ ''
+ );
+ }
+}
diff --git a/app/Http/Routes/WebRoutes.php b/app/Http/Routes/WebRoutes.php
index c150c19bce..a13dfe5a27 100644
--- a/app/Http/Routes/WebRoutes.php
+++ b/app/Http/Routes/WebRoutes.php
@@ -177,6 +177,8 @@ use Fisharebest\Webtrees\Http\RequestHandlers\ModulesBlocksAction;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesBlocksPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesChartsAction;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesChartsPage;
+use Fisharebest\Webtrees\Http\RequestHandlers\ModulesCustomTagsAction;
+use Fisharebest\Webtrees\Http\RequestHandlers\ModulesCustomTagsPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesDataFixesAction;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesDataFixesPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesFootersAction;
@@ -386,6 +388,8 @@ class WebRoutes
$router->post(ModulesBlocksAction::class, '/blocks');
$router->get(ModulesChartsPage::class, '/charts');
$router->post(ModulesChartsAction::class, '/charts');
+ $router->get(ModulesCustomTagsPage::class, '/custom-tags');
+ $router->post(ModulesCustomTagsAction::class, '/custom-tags');
$router->get(ModulesDataFixesPage::class, '/data-fixes');
$router->post(ModulesDataFixesAction::class, '/data-fixes');
$router->get(ModulesFootersPage::class, '/footers');
diff --git a/app/Module/CustomTagsAldfaer.php b/app/Module/CustomTagsAldfaer.php
new file mode 100644
index 0000000000..37eafcbcbb
--- /dev/null
+++ b/app/Module/CustomTagsAldfaer.php
@@ -0,0 +1,56 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CustomEvent;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsAldfaer
+ */
+class CustomTagsAldfaer extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:MARR_CIVIL' => new CustomEvent(I18N::translate('Civil marriage')),
+ 'FAM:MARR_RELIGIOUS' => new CustomEvent(I18N::translate('Religious marriage')),
+ 'FAM:MARR_PARTNERS' => new CustomEvent(I18N::translate('Registered partnership')),
+ 'FAM:MARR_UNKNOWN' => new CustomEvent(I18N::translate('Marriage type unknown')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Aldfaer™';
+ }
+}
diff --git a/app/Module/CustomTagsAncestry.php b/app/Module/CustomTagsAncestry.php
new file mode 100644
index 0000000000..9d0430cc42
--- /dev/null
+++ b/app/Module/CustomTagsAncestry.php
@@ -0,0 +1,55 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\CustomEvent;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsAncestry
+ */
+class CustomTagsAncestry extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'INDI:*:SOUR:_APID' => new CustomElement(I18N::translate('Ancestry PID')),
+ 'INDI:_EMPLOY' => new CustomEvent(I18N::translate('Occupation')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Ancestry™';
+ }
+}
diff --git a/app/Module/CustomTagsBrothersKeeper.php b/app/Module/CustomTagsBrothersKeeper.php
new file mode 100644
index 0000000000..2aecdb943b
--- /dev/null
+++ b/app/Module/CustomTagsBrothersKeeper.php
@@ -0,0 +1,117 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\AddressEmail;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\CustomEvent;
+use Fisharebest\Webtrees\Elements\CustomFact;
+use Fisharebest\Webtrees\Elements\DateValue;
+use Fisharebest\Webtrees\Elements\NamePersonal;
+use Fisharebest\Webtrees\Elements\PlaceName;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Custom tags created by Brother’s Keeper
+ *
+ * Class CustomTagsBrothersKeeper
+ */
+class CustomTagsBrothersKeeper extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ *
+ * @see http://wiki-de.genealogy.net/GEDCOM/_Nutzerdef-Tag
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:*:_EVN' => new CustomElement('Event number'),
+ 'FAM:CHIL:_FREL' => new CustomElement('Relationship to father'),
+ 'FAM:CHIL:_MREL' => new CustomElement('Relationship to mother'),
+ 'FAM:_COML' => new CustomEvent(I18N::translate('Common law marriage')),
+ 'FAM:_MARI' => new CustomEvent(I18N::translate('Marriage intention')),
+ 'FAM:_MBON' => new CustomEvent(I18N::translate('Marriage bond')),
+ 'FAM:_NMR' => new CustomEvent(I18N::translate('Not married'), ['NOTE' => '0:M', 'SOUR' => '0:M']),
+ 'FAM:_PRMN' => new CustomElement(I18N::translate('Permanent number')),
+ 'FAM:_SEPR' => new CustomEvent(I18N::translate('Separated')),
+ 'FAM:_TODO' => new CustomElement(I18N::translate('Research task')),
+ 'INDI:*:_EVN' => new CustomElement('Event number'),
+ 'INDI:NAME:_ADPN' => new NamePersonal(I18N::translate('Adopted name'), []),
+ 'INDI:NAME:_AKAN' => new NamePersonal(I18N::translate('Also known as'), []),
+ 'INDI:NAME:_BIRN' => new NamePersonal(I18N::translate('Birth name'), []),
+ 'INDI:NAME:_CALL' => new NamePersonal('Called name', []),
+ 'INDI:NAME:_CENN' => new NamePersonal('Census name', []),
+ 'INDI:NAME:_CURN' => new NamePersonal('Current name', []),
+ 'INDI:NAME:_FARN' => new NamePersonal(I18N::translate('Estate name'), []),
+ 'INDI:NAME:_FKAN' => new NamePersonal('Formal name', []),
+ 'INDI:NAME:_FRKA' => new NamePersonal('Formerly known as', []),
+ 'INDI:NAME:_GERN' => new NamePersonal('German name', []),
+ 'INDI:NAME:_HEBN' => new NamePersonal(I18N::translate('Hebrew name'), []),
+ 'INDI:NAME:_HNM' => new NamePersonal(I18N::translate('Hebrew name'), []),
+ 'INDI:NAME:_INDG' => new NamePersonal('Indigenous name', []),
+ 'INDI:NAME:_INDN' => new NamePersonal('Indian name', []),
+ 'INDI:NAME:_LNCH' => new NamePersonal('Legal name change', []),
+ 'INDI:NAME:_MARN' => new NamePersonal('Married name', []),
+ 'INDI:NAME:_MARNM' => new NamePersonal('Married name', []),
+ 'INDI:NAME:_OTHN' => new NamePersonal('Other name', []),
+ 'INDI:NAME:_RELN' => new NamePersonal('Religious name', []),
+ 'INDI:NAME:_SHON' => new NamePersonal('Short name', []),
+ 'INDI:NAME:_SLDN' => new NamePersonal('Soldier name', []),
+ 'INDI:_ADPF' => new CustomElement(I18N::translate('Adopted by father')),
+ 'INDI:_ADPM' => new CustomElement(I18N::translate('Adopted by mother')),
+ 'INDI:_BRTM' => new CustomEvent(I18N::translate('Brit milah')),
+ 'INDI:_BRTM:DATE' => new DateValue(I18N::translate('Date of brit milah')),
+ 'INDI:_BRTM:PLAC' => new PlaceName(I18N::translate('Place of brit milah')),
+ 'INDI:_EMAIL' => new AddressEmail(I18N::translate('Email address')),
+ 'INDI:_EYEC' => new CustomFact(I18N::translate('Eye color')),
+ 'INDI:_FRNL' => new CustomElement(I18N::translate('Funeral')),
+ 'INDI:_HAIR' => new CustomFact(I18N::translate('Hair color')),
+ 'INDI:_HEIG' => new CustomFact(I18N::translate('Height')),
+ 'INDI:_INTE' => new CustomElement(I18N::translate('Interment')),
+ 'INDI:_MEDC' => new CustomFact(I18N::translate('Medical')),
+ 'INDI:_MILT' => new CustomElement(I18N::translate('Military service')),
+ 'INDI:_NLIV' => new CustomFact(I18N::translate('Not living')),
+ 'INDI:_NMAR' => new CustomEvent(I18N::translate('Never married'), ['NOTE' => '0:M', 'SOUR' => '0:M']),
+ 'INDI:_PRMN' => new CustomElement(I18N::translate('Permanent number')),
+ 'INDI:_TODO' => new CustomElement(I18N::translate('Research task')),
+ 'INDI:_WEIG' => new CustomFact(I18N::translate('Weight')),
+ 'INDI:_YART' => new CustomEvent(I18N::translate('Yahrzeit')),
+ // 1 XXXX
+ // 2 _EVN ##
+ // 1 ASSO @Xnnn@
+ // 2 RELA Witness at event _EVN ##
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Brother’s Keeper™';
+ }
+}
diff --git a/app/Module/CustomTagsFamilySearch.php b/app/Module/CustomTagsFamilySearch.php
new file mode 100644
index 0000000000..92a956054b
--- /dev/null
+++ b/app/Module/CustomTagsFamilySearch.php
@@ -0,0 +1,53 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\FamilySearchFamilyTreeId;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsFamilySearch
+ */
+class CustomTagsFamilySearch extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'INDI:_FSFTID' => /* I18N: familysearch.org */ new FamilySearchFamilyTreeId(I18N::translate('FamilySearch ID')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'FamilySearch';
+ }
+}
diff --git a/app/Module/CustomTagsFamilyTreeBuilder.php b/app/Module/CustomTagsFamilyTreeBuilder.php
new file mode 100644
index 0000000000..9264ad9a0f
--- /dev/null
+++ b/app/Module/CustomTagsFamilyTreeBuilder.php
@@ -0,0 +1,64 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\DateValue;
+use Fisharebest\Webtrees\Elements\NamePersonal;
+use Fisharebest\Webtrees\Elements\PlaceName;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsFTB
+ */
+class CustomTagsFamilyTreeBuilder extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ '*:_UPD' => new CustomElement(I18N::translate('Last change')), // e.g. "1 _UPD 14 APR 2012 00:14:10 GMT-5"
+ 'INDI:NAME:_AKA' => new NamePersonal(I18N::translate('Also known as'), []),
+ 'OBJE:_ALBUM' => new CustomElement(I18N::translate('Album')), // XREF to an album
+ 'OBJE:_DATE' => new DateValue(I18N::translate('Date')),
+ 'OBJE:_FILESIZE' => new CustomElement(I18N::translate('File size')),
+ 'OBJE:_PHOTO_RIN' => new CustomElement(I18N::translate('Photo')),
+ 'OBJE:_PLACE' => new PlaceName(I18N::translate('Place')),
+ '_ALBUM:_PHOTO' => new CustomElement(I18N::translate('Photo')),
+ '_ALBUM:_PHOTO:_PRIN' => new CustomElement(I18N::translate('Highlighted image')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Family Tree Builder™';
+ }
+}
diff --git a/app/Module/CustomTagsFamilyTreeMaker.php b/app/Module/CustomTagsFamilyTreeMaker.php
new file mode 100644
index 0000000000..eb19e22fd1
--- /dev/null
+++ b/app/Module/CustomTagsFamilyTreeMaker.php
@@ -0,0 +1,148 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\NamePersonal;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Custom tags created by FamilyTreeMaker (DOS, Windows, ancestry.com)
+ *
+ * Class CustomTagsFamilyTreeMaker
+ */
+class CustomTagsFamilyTreeMaker extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ *
+ * @see http://wiki-de.genealogy.net/GEDCOM/_Nutzerdef-Tag
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:CHIL:_FREL' => new CustomElement(I18N::translate('Relationship to father')),
+ 'FAM:CHIL:_MREL' => new CustomElement(I18N::translate('Relationship to mother')),
+ 'FAM:_DETS' => new CustomElement(I18N::translate('Death of one spouse')),
+ 'FAM:_FA1' => new CustomElement(I18N::translate('Fact 1')),
+ 'FAM:_FA10' => new CustomElement(I18N::translate('Fact 10')),
+ 'FAM:_FA11' => new CustomElement(I18N::translate('Fact 11')),
+ 'FAM:_FA12' => new CustomElement(I18N::translate('Fact 12')),
+ 'FAM:_FA13' => new CustomElement(I18N::translate('Fact 13')),
+ 'FAM:_FA2' => new CustomElement(I18N::translate('Fact 2')),
+ 'FAM:_FA3' => new CustomElement(I18N::translate('Fact 3')),
+ 'FAM:_FA4' => new CustomElement(I18N::translate('Fact 4')),
+ 'FAM:_FA5' => new CustomElement(I18N::translate('Fact 5')),
+ 'FAM:_FA6' => new CustomElement(I18N::translate('Fact 6')),
+ 'FAM:_FA7' => new CustomElement(I18N::translate('Fact 7')),
+ 'FAM:_FA8' => new CustomElement(I18N::translate('Fact 8')),
+ 'FAM:_FA9' => new CustomElement(I18N::translate('Fact 9')),
+ 'FAM:_MEND' => new CustomElement(I18N::translate('Marriage ending status')),
+ 'FAM:_MSTAT' => new CustomElement(I18N::translate('Marriage beginning status')),
+ 'FAM:_SEPR' => new CustomElement(I18N::translate('Separation')),
+ 'HEAD:_SCHEMA' => new CustomElement('Schema'),
+ 'HEAD:_SCHEMA:FAM' => new CustomElement(I18N::translate('Family')),
+ 'HEAD:_SCHEMA:FAM:_FA*:LABL' => new CustomElement(I18N::translate('Label')),
+ 'HEAD:_SCHEMA:FAM:_FA1' => new CustomElement(I18N::translate('Fact 1')),
+ 'HEAD:_SCHEMA:FAM:_FA10' => new CustomElement(I18N::translate('Fact 10')),
+ 'HEAD:_SCHEMA:FAM:_FA11' => new CustomElement(I18N::translate('Fact 11')),
+ 'HEAD:_SCHEMA:FAM:_FA12' => new CustomElement(I18N::translate('Fact 12')),
+ 'HEAD:_SCHEMA:FAM:_FA13' => new CustomElement(I18N::translate('Fact 13')),
+ 'HEAD:_SCHEMA:FAM:_FA2' => new CustomElement(I18N::translate('Fact 2')),
+ 'HEAD:_SCHEMA:FAM:_FA3' => new CustomElement(I18N::translate('Fact 3')),
+ 'HEAD:_SCHEMA:FAM:_FA4' => new CustomElement(I18N::translate('Fact 4')),
+ 'HEAD:_SCHEMA:FAM:_FA5' => new CustomElement(I18N::translate('Fact 5')),
+ 'HEAD:_SCHEMA:FAM:_FA6' => new CustomElement(I18N::translate('Fact 6')),
+ 'HEAD:_SCHEMA:FAM:_FA7' => new CustomElement(I18N::translate('Fact 7')),
+ 'HEAD:_SCHEMA:FAM:_FA8' => new CustomElement(I18N::translate('Fact 8')),
+ 'HEAD:_SCHEMA:FAM:_FA9' => new CustomElement(I18N::translate('Fact 9')),
+ 'HEAD:_SCHEMA:FAM:_M*:LABL' => new CustomElement(I18N::translate('Label')),
+ 'HEAD:_SCHEMA:FAM:_MEND' => new CustomElement(I18N::translate('Marriage ending status')),
+ 'HEAD:_SCHEMA:FAM:_MSTAT' => new CustomElement(I18N::translate('Marriage beginning status')),
+ 'HEAD:_SCHEMA:INDI' => new CustomElement(I18N::translate('Individual')),
+ 'HEAD:_SCHEMA:INDI:_FA*:LABL' => new CustomElement(I18N::translate('Label')),
+ 'HEAD:_SCHEMA:INDI:_FA1' => new CustomElement(I18N::translate('Fact 1')),
+ 'HEAD:_SCHEMA:INDI:_FA10' => new CustomElement(I18N::translate('Fact 10')),
+ 'HEAD:_SCHEMA:INDI:_FA11' => new CustomElement(I18N::translate('Fact 11')),
+ 'HEAD:_SCHEMA:INDI:_FA12' => new CustomElement(I18N::translate('Fact 12')),
+ 'HEAD:_SCHEMA:INDI:_FA13' => new CustomElement(I18N::translate('Fact 13')),
+ 'HEAD:_SCHEMA:INDI:_FA2' => new CustomElement(I18N::translate('Fact 2')),
+ 'HEAD:_SCHEMA:INDI:_FA3' => new CustomElement(I18N::translate('Fact 3')),
+ 'HEAD:_SCHEMA:INDI:_FA4' => new CustomElement(I18N::translate('Fact 4')),
+ 'HEAD:_SCHEMA:INDI:_FA5' => new CustomElement(I18N::translate('Fact 5')),
+ 'HEAD:_SCHEMA:INDI:_FA6' => new CustomElement(I18N::translate('Fact 6')),
+ 'HEAD:_SCHEMA:INDI:_FA7' => new CustomElement(I18N::translate('Fact 7')),
+ 'HEAD:_SCHEMA:INDI:_FA8' => new CustomElement(I18N::translate('Fact 8')),
+ 'HEAD:_SCHEMA:INDI:_FA9' => new CustomElement(I18N::translate('Fact 9')),
+ 'HEAD:_SCHEMA:INDI:_FREL' => new CustomElement('Relationship to father'),
+ 'HEAD:_SCHEMA:INDI:_M*:LABL' => new CustomElement(I18N::translate('Label')),
+ 'HEAD:_SCHEMA:INDI:_MREL' => new CustomElement('Relationship to mother'),
+ 'INDI:*:SOUR:_APID' => new CustomElement('Ancestry.com source identifier'),
+ 'INDI:*:SOUR:_LINK' => new CustomElement('External link'),
+ 'INDI:NAME:_AKA' => new NamePersonal(I18N::translate('Also known as'), []),
+ 'INDI:NAME:_MARNM' => new NamePersonal(I18N::translate('Married name'), []),
+ 'INDI:_CIRC' => new CustomElement('Circumcision'),
+ 'INDI:_DCAUSE' => new CustomElement(I18N::translate('Cause of death')),
+ 'INDI:_DEG' => new CustomElement(I18N::translate('Degree')),
+ 'INDI:_DNA' => new CustomElement(I18N::translate('DNA markers')),
+ 'INDI:_ELEC' => new CustomElement('Elected'),
+ 'INDI:_EMPLOY' => new CustomElement('Employment'),
+ 'INDI:_EXCM' => new CustomElement('Excommunicated'),
+ 'INDI:_FA1' => new CustomElement('Fact 1'),
+ 'INDI:_FA10' => new CustomElement('Fact 10'),
+ 'INDI:_FA11' => new CustomElement('Fact 11'),
+ 'INDI:_FA12' => new CustomElement('Fact 12'),
+ 'INDI:_FA13' => new CustomElement('Fact 13'),
+ 'INDI:_FA2' => new CustomElement('Fact 2'),
+ 'INDI:_FA3' => new CustomElement('Fact 3'),
+ 'INDI:_FA4' => new CustomElement('Fact 4'),
+ 'INDI:_FA5' => new CustomElement('Fact 5'),
+ 'INDI:_FA6' => new CustomElement('Fact 6'),
+ 'INDI:_FA7' => new CustomElement('Fact 7'),
+ 'INDI:_FA8' => new CustomElement('Fact 8'),
+ 'INDI:_FA9' => new CustomElement('Fact 9'),
+ 'INDI:_MDCL' => new CustomElement('Medical'),
+ 'INDI:_MILT' => new CustomElement(I18N::translate('Military service')),
+ 'INDI:_MILTID' => new CustomElement('Military ID number'),
+ 'INDI:_MISN' => new CustomElement('Mission'),
+ 'INDI:_NAMS' => new CustomElement(I18N::translate('Namesake')),
+ 'INDI:_UNKN' => new CustomElement(I18N::translate('Unknown')), // Special individual ID code for later file comparisons
+ // The context and meaning of these tags is unknown
+ '_FOOT' => new CustomElement(''),
+ '_FUN' => new CustomElement(''),
+ '_JUST' => new CustomElement(''),
+ '_PHOTO' => new CustomElement(''),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Family Tree Maker™';
+ }
+}
diff --git a/app/Module/CustomTagsGedcom53.php b/app/Module/CustomTagsGedcom53.php
new file mode 100644
index 0000000000..716f3b3536
--- /dev/null
+++ b/app/Module/CustomTagsGedcom53.php
@@ -0,0 +1,191 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CertaintyAssessment;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\DateValue;
+use Fisharebest\Webtrees\Elements\NoteStructure;
+use Fisharebest\Webtrees\Elements\PlaceName;
+use Fisharebest\Webtrees\Elements\SourceMediaType;
+use Fisharebest\Webtrees\Elements\TextFromSource;
+use Fisharebest\Webtrees\Elements\XrefSource;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsGedcom53
+ */
+class CustomTagsGedcom53 extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'EVEN' => new CustomElement('Event'),
+ 'EVEN:*:*:NAME' => new CustomElement(I18N::translate('Name')),
+ 'EVEN:*:AUDIO' => new CustomElement(I18N::translate('Audio')),
+ 'EVEN:*:BROT' => new PlaceName('Brother'),
+ 'EVEN:*:BUYR' => new PlaceName('Buyer'),
+ 'EVEN:*:CHIL' => new PlaceName('Child'),
+ 'EVEN:*:DATE' => new DateValue('Date'),
+ 'EVEN:*:FATH' => new PlaceName('Father'),
+ 'EVEN:*:GODP' => new PlaceName('Godparent'),
+ 'EVEN:*:HDOH' => new PlaceName('Head of household'),
+ 'EVEN:*:HEIR' => new PlaceName('Heir'),
+ 'EVEN:*:HFAT' => new PlaceName('Husband’s father'),
+ 'EVEN:*:HMOT' => new PlaceName('Husband’s mother'),
+ 'EVEN:*:HUSB' => new PlaceName('Husband'),
+ 'EVEN:*:IMAGE' => new CustomElement('Image'),
+ 'EVEN:*:INDI' => new PlaceName('Individual'),
+ 'EVEN:*:INFT' => new PlaceName('Informant'),
+ 'EVEN:*:LEGA' => new PlaceName('Legatee'),
+ 'EVEN:*:MBR' => new PlaceName('Member'),
+ 'EVEN:*:MOTH' => new PlaceName('Mother'),
+ 'EVEN:*:OFFI' => new PlaceName('Official'),
+ 'EVEN:*:PARE' => new PlaceName('Parent'),
+ 'EVEN:*:PHOTO' => new CustomElement(I18N::translate('Photo')),
+ 'EVEN:*:PHUS' => new PlaceName('Previous husband'),
+ 'EVEN:*:PLAC' => new PlaceName('Place'),
+ 'EVEN:*:PWIF' => new PlaceName('Previous wife'),
+ 'EVEN:*:RECO' => new PlaceName('Recorder'),
+ 'EVEN:*:REL' => new PlaceName('Relative'),
+ 'EVEN:*:SELR' => new PlaceName('Seller'),
+ 'EVEN:*:SIBL' => new PlaceName('Sibling'),
+ 'EVEN:*:SIST' => new PlaceName('Sister'),
+ 'EVEN:*:SPOU' => new PlaceName('Spouse'),
+ 'EVEN:*:TXPY' => new PlaceName('Taxpayer'),
+ 'EVEN:*:VIDEO' => new CustomElement(I18N::translate('Video')),
+ 'EVEN:*:WFAT' => new PlaceName('Wife’s father'),
+ 'EVEN:*:WIFE' => new PlaceName('Wife'),
+ 'EVEN:*:WITN' => new PlaceName('Witness'),
+ 'EVEN:*:WMOT' => new PlaceName('Wife’s mother'),
+ 'EVEN:TYPE' => new CustomElement('Type of event'),
+ 'FAM:*:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'FAM:*:PLAC:SITE' => new CustomElement('Site'),
+ 'FAM:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'FAM:AUDIO' => new CustomElement(I18N::translate('Audio')),
+ 'FAM:IMAGE' => new CustomElement('Image'),
+ 'FAM:PHOTO' => new CustomElement(I18N::translate('Photo')),
+ 'FAM:VIDEO' => new CustomElement(I18N::translate('Video')),
+ 'HEAD:SCHEMA' => new CustomElement(I18N::translate('Unknown')),
+ 'HEAD:SCHEMA:FAM' => new CustomElement(I18N::translate('Family')),
+ 'HEAD:SCHEMA:FAM:*:_*' => new CustomElement('Custom event'),
+ 'HEAD:SCHEMA:FAM:*:_*:DEFN' => new CustomElement('Definition'),
+ 'HEAD:SCHEMA:FAM:*:_*:ISA' => new CustomElement('Type of event'),
+ 'HEAD:SCHEMA:FAM:*:_*:LABL' => new CustomElement('Label'),
+ 'HEAD:SCHEMA:FAM:_*' => new CustomElement('Custom event'),
+ 'HEAD:SCHEMA:FAM:_*:DEFN' => new CustomElement('Definition'),
+ 'HEAD:SCHEMA:FAM:_*:ISA' => new CustomElement('Type of event'),
+ 'HEAD:SCHEMA:FAM:_*:LABL' => new CustomElement('Label'),
+ 'HEAD:SCHEMA:INDI' => new CustomElement(I18N::translate('Individual')),
+ 'HEAD:SCHEMA:INDI:*:_*' => new CustomElement('Custom event'),
+ 'HEAD:SCHEMA:INDI:*:_*:DEFN' => new CustomElement('Definition'),
+ 'HEAD:SCHEMA:INDI:*:_*:ISA' => new CustomElement('Type of event'),
+ 'HEAD:SCHEMA:INDI:*:_*:LABL' => new CustomElement('Label'),
+ 'HEAD:SCHEMA:INDI:_*' => new CustomElement('Custom event'),
+ 'HEAD:SCHEMA:INDI:_*:DEFN' => new CustomElement('Definition'),
+ 'HEAD:SCHEMA:INDI:_*:ISA' => new CustomElement('Type of event'),
+ 'HEAD:SCHEMA:INDI:_*:LABL' => new CustomElement('Label'),
+ 'INDI:*:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'INDI:*:PLAC:SITE' => new CustomElement('Site'),
+ 'INDI:*:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'INDI:AUDIO' => new CustomElement(I18N::translate('Audio')),
+ 'INDI:BURI:PLAC:CEME' => new CustomElement(I18N::translate('Cemetery')),
+ 'INDI:BURI:PLAC:CEME:PLOT' => new CustomElement('Burial plot'),
+ 'INDI:IMAGE' => new CustomElement('Image'),
+ 'INDI:NAMR' => new CustomElement(I18N::translate('Religious name')),
+ 'INDI:NAMS' => new CustomElement(I18N::translate('Namesake')),
+ 'INDI:PHOTO' => new CustomElement(I18N::translate('Photo')),
+ 'INDI:SIGN' => new CustomElement('Signature'),
+ 'INDI:VIDEO' => new CustomElement(I18N::translate('Video')),
+ 'REPO:CALN:ITEM' => new CustomElement('Item'),
+ 'REPO:CALN:PAGE' => new CustomElement('Page'),
+ 'REPO:CALN:SHEE' => new CustomElement('Sheet'),
+ 'REPO:CNTC' => new CustomElement('Contact person'),
+ 'REPO:MEDI' => new SourceMediaType(I18N::translate('Media type')),
+ 'REPO:REFN' => new CustomElement('Reference number'),
+ 'SOUR:AUDIO' => new CustomElement(I18N::translate('Audio')),
+ 'SOUR:CENS' => new CustomElement('Census'),
+ 'SOUR:CENS:DATE' => new CustomElement('Census'),
+ 'SOUR:CENS:DWEL' => new CustomElement('Dwelling number'),
+ 'SOUR:CENS:FAMN' => new CustomElement('Family number'),
+ 'SOUR:CENS:LINE' => new CustomElement('Line number'),
+ 'SOUR:CLAS' => new CustomElement('Source classification'),
+ 'SOUR:CPLR' => new CustomElement('Compiler'),
+ 'SOUR:EDTR' => new CustomElement('Editor'),
+ 'SOUR:EVEN' => new CustomElement('Source events'),
+ 'SOUR:FIDE' => new CustomElement('Fidelity'),
+ 'SOUR:FILM' => new CustomElement(I18N::translate('Microfilm')),
+ 'SOUR:IMAGE' => new CustomElement('Image'),
+ 'SOUR:INDX' => new CustomElement('Indexed'),
+ 'SOUR:INTV' => new CustomElement('Interviewer'),
+ 'SOUR:ORIG' => new CustomElement('Originator'),
+ 'SOUR:ORIG:NAME' => new CustomElement('Name'),
+ 'SOUR:ORIG:NOTE' => new CustomElement('Note'),
+ 'SOUR:ORIG:TYPE' => new CustomElement('Type'),
+ 'SOUR:PERI' => new CustomElement('Date period'),
+ 'SOUR:PHOTO' => new CustomElement(I18N::translate('Photo')),
+ 'SOUR:PUBL:DATE' => new CustomElement('Date'),
+ 'SOUR:PUBL:EDTN' => new CustomElement('Edition'),
+ 'SOUR:PUBL:ISSU' => new CustomElement('Issue'),
+ 'SOUR:PUBL:LCCN' => new CustomElement('Library of Congress call number'),
+ 'SOUR:PUBL:NAME' => new CustomElement('Name'),
+ 'SOUR:PUBL:PUBR' => new CustomElement('Publisher'),
+ 'SOUR:PUBL:SERS' => new CustomElement('Series'),
+ 'SOUR:PUBL:TYPE' => new CustomElement('Type'),
+ 'SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'SOUR:RECO' => new CustomElement('Recording agency?'),
+ 'SOUR:REFS' => new XrefSource('Referenced source'),
+ 'SOUR:REPO:DPRT:ARVL' => new CustomElement('Departure'),
+ 'SOUR:REPO:DPRT:ARVL:DATE' => new DateValue('Date'),
+ 'SOUR:REPO:DPRT:ARVL:PLAC' => new PlaceName('Place'),
+ 'SOUR:REPO:NAME' => new CustomElement('Name of vessel'),
+ 'SOUR:REPO:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'SOUR:REPO:PORT' => new CustomElement('Port'),
+ 'SOUR:REPO:PORT:ARVL' => new CustomElement('Arrival'),
+ 'SOUR:REPO:PORT:ARVL:DATE' => new DateValue('Date'),
+ 'SOUR:REPO:PORT:ARVL:PLAC' => new PlaceName('Place'),
+ 'SOUR:REPO:TEXT' => new TextFromSource(I18N::translate('Text')),
+ 'SOUR:SEQU' => new CustomElement('Sequence'),
+ 'SOUR:STAT' => new CustomElement('Search status'),
+ 'SOUR:STAT:DATE' => new DateValue('Date'),
+ 'SOUR:TEXT' => new TextFromSource(I18N::translate('Text')),
+ 'SOUR:TYPE' => new CustomElement('Type of source'),
+ 'SOUR:VIDEO' => new CustomElement(I18N::translate('Video')),
+ 'SOUR:XLTR' => new CustomElement('Translator'),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'GEDCOM 5.3';
+ }
+}
diff --git a/app/Module/CustomTagsGedcom55.php b/app/Module/CustomTagsGedcom55.php
new file mode 100644
index 0000000000..7e671e0fcf
--- /dev/null
+++ b/app/Module/CustomTagsGedcom55.php
@@ -0,0 +1,53 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsGedcom55
+ */
+class CustomTagsGedcom55 extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'OBJE:BLOB' => new CustomElement(I18N::translate('Binary data object')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'GEDCOM 5.5';
+ }
+}
diff --git a/app/Module/CustomTagsGedcomL.php b/app/Module/CustomTagsGedcomL.php
new file mode 100644
index 0000000000..eee0bdcb16
--- /dev/null
+++ b/app/Module/CustomTagsGedcomL.php
@@ -0,0 +1,248 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\AddressPostalCode;
+use Fisharebest\Webtrees\Elements\CertaintyAssessment;
+use Fisharebest\Webtrees\Elements\Change;
+use Fisharebest\Webtrees\Elements\ChangeDate;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\DateValue;
+use Fisharebest\Webtrees\Elements\EmptyElement;
+use Fisharebest\Webtrees\Elements\EntryRecordingDate;
+use Fisharebest\Webtrees\Elements\EventAttributeType;
+use Fisharebest\Webtrees\Elements\EventDescriptor;
+use Fisharebest\Webtrees\Elements\EventTypeCitedFrom;
+use Fisharebest\Webtrees\Elements\FamilyStatusText;
+use Fisharebest\Webtrees\Elements\GovIdentifier;
+use Fisharebest\Webtrees\Elements\HierarchicalRelationship;
+use Fisharebest\Webtrees\Elements\LanguageId;
+use Fisharebest\Webtrees\Elements\LocationRecord;
+use Fisharebest\Webtrees\Elements\MaidenheadLocator;
+use Fisharebest\Webtrees\Elements\NamePieceGiven;
+use Fisharebest\Webtrees\Elements\NoteStructure;
+use Fisharebest\Webtrees\Elements\PafUid;
+use Fisharebest\Webtrees\Elements\PlaceLatitude;
+use Fisharebest\Webtrees\Elements\PlaceLongtitude;
+use Fisharebest\Webtrees\Elements\PlaceName;
+use Fisharebest\Webtrees\Elements\RelationIsDescriptor;
+use Fisharebest\Webtrees\Elements\ReligiousAffiliation;
+use Fisharebest\Webtrees\Elements\ResearchTask;
+use Fisharebest\Webtrees\Elements\ResearchTaskPriority;
+use Fisharebest\Webtrees\Elements\ResearchTaskStatus;
+use Fisharebest\Webtrees\Elements\ResearchTaskType;
+use Fisharebest\Webtrees\Elements\RoleInEvent;
+use Fisharebest\Webtrees\Elements\SexXValue;
+use Fisharebest\Webtrees\Elements\SourceData;
+use Fisharebest\Webtrees\Elements\SubmitterText;
+use Fisharebest\Webtrees\Elements\TextFromSource;
+use Fisharebest\Webtrees\Elements\TimeValue;
+use Fisharebest\Webtrees\Elements\WhereWithinSource;
+use Fisharebest\Webtrees\Elements\XrefAssociate;
+use Fisharebest\Webtrees\Elements\XrefLocation;
+use Fisharebest\Webtrees\Elements\XrefMedia;
+use Fisharebest\Webtrees\Elements\XrefRepository;
+use Fisharebest\Webtrees\Elements\XrefSource;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsGedcomL
+ *
+ * @see https://genealogy.net/GEDCOM/GEDCOM551%20GEDCOM-L%20Addendum-R1.pdf
+ */
+class CustomTagsGedcomL extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:*:ADDR:_NAME' => new CustomElement('Name of addressee'),
+ 'FAM:*:PLAC:_GOV' => new GovIdentifier(I18N::translate('GOV identifier')),
+ 'FAM:*:PLAC:_LOC' => new XrefLocation(I18N::translate('Location')),
+ 'FAM:*:PLAC:_MAIDENHEAD' => new MaidenheadLocator('Maidenhead locator'),
+ 'FAM:*:PLAC:_POST' => new AddressPostalCode('Postal code'),
+ 'FAM:*:PLAC:_POST:DATE' => new DateValue(I18N::translate('Date')),
+ 'FAM:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
+ 'FAM:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'FAM:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
+ 'FAM:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
+ 'FAM:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
+ 'FAM:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
+ 'FAM:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
+ 'FAM:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
+ 'FAM:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
+ 'FAM:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'FAM:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
+ 'FAM:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
+ 'FAM:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'FAM:*:_WITN' => new CustomElement('Witness'),
+ 'FAM:_STAT' => new FamilyStatusText(I18N::translate('Family status')),
+ 'FAM:_TODO' => new ResearchTask(I18N::translate('Research task'), ['DESC' => '1:1', '_CAT' => '0:1', '_PRTY' => '0:1', 'TYPE' => '0:1', 'NOTE' => '0:M', 'DATA' => '0:1', 'STAT' => '0:1', '_CDATE' => '0:1', '_RDATE' => '0:1', 'REPO' => '0:1', '_UID' => '0:M']),
+ 'FAM:_TODO:DATA' => new SubmitterText(I18N::translate('The solution')),
+ 'FAM:_TODO:DATE' => new DateValue(I18N::translate('Creation date')),
+ 'FAM:_TODO:DESC' => new CustomElement(I18N::translate('Description')),
+ 'FAM:_TODO:NOTE' => new SubmitterText(I18N::translate('The problem')),
+ 'FAM:_TODO:REPO' => new XrefRepository('Repository', []),
+ 'FAM:_TODO:STAT' => new ResearchTaskStatus(I18N::translate('Status')),
+ 'FAM:_TODO:TYPE' => new ResearchTaskType(I18N::translate('Type of research task')),
+ 'FAM:_TODO:_CAT' => new CustomElement(I18N::translate('Category')),
+ 'FAM:_TODO:_CDATE' => new DateValue(I18N::translate('Completion date')),
+ 'FAM:_TODO:_PRTY' => new ResearchTaskPriority(I18N::translate('Priority')),
+ 'FAM:_TODO:_RDATE' => new DateValue(I18N::translate('Reminder date')),
+ 'FAM:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'HEAD:SOUR:CORP:ADDR:_NAME' => new CustomElement('Name of addressee'),
+ 'HEAD:_SCHEMA' => new EmptyElement(I18N::translate('Schema')),
+ 'HEAD:_SCHEMA:*' => new EmptyElement(I18N::translate('Base GEDCOM tag')),
+ 'HEAD:_SCHEMA:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
+ 'HEAD:_SCHEMA:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
+ 'HEAD:_SCHEMA:*:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
+ 'HEAD:_SCHEMA:*:*:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
+ 'HEAD:_SCHEMA:*:*:*:*:*:*' => new EmptyElement(I18N::translate('New GEDCOM tag')),
+ 'HEAD:_SCHEMA:*:*:*:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
+ 'HEAD:_SCHEMA:*:*:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
+ 'HEAD:_SCHEMA:*:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
+ 'HEAD:_SCHEMA:*:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
+ 'HEAD:_SCHEMA:*:*:_DEFN' => new EmptyElement(I18N::translate('Definition')),
+ 'INDI:*:ADDR:_NAME' => new CustomElement('Name of addressee'),
+ 'INDI:*:PLAC:_GOV' => new GovIdentifier(I18N::translate('GOV identifier')),
+ 'INDI:*:PLAC:_LOC' => new XrefLocation(I18N::translate('Location')),
+ 'INDI:*:PLAC:_MAIDENHEAD' => new MaidenheadLocator('Maidenhead locator'),
+ 'INDI:*:PLAC:_POST' => new AddressPostalCode('Postal code'),
+ 'INDI:*:PLAC:_POST:DATE' => new DateValue(I18N::translate('Date')),
+ 'INDI:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
+ 'INDI:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'INDI:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
+ 'INDI:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
+ 'INDI:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
+ 'INDI:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
+ 'INDI:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
+ 'INDI:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
+ 'INDI:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
+ 'INDI:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'INDI:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
+ 'INDI:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
+ 'INDI:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'INDI:*:_WITN' => new CustomElement('Witness'),
+ 'INDI:BAPM:_GODP' => new CustomElement('Godparent'),
+ 'INDI:CHR:_GODP' => new CustomElement('Godparent'),
+ 'INDI:NAME:_RUFNAME' => new NamePieceGiven(I18N::translate('Rufname')),
+ 'INDI:OBJE:_PRIM' => new CustomElement(I18N::translate('Highlighted image')),
+ 'INDI:SEX' => new SexXValue(I18N::translate('Gender')),
+ 'INDI:_TODO' => new ResearchTask(I18N::translate('Research task')),
+ 'INDI:_TODO:DATA' => new SubmitterText(I18N::translate('The solution')),
+ 'INDI:_TODO:DATE' => new DateValue(I18N::translate('Creation date')),
+ 'INDI:_TODO:DESC' => new CustomElement(I18N::translate('Description')),
+ 'INDI:_TODO:NOTE' => new SubmitterText(I18N::translate('The problem')),
+ 'INDI:_TODO:REPO' => new XrefRepository('Repository', []),
+ 'INDI:_TODO:STAT' => new ResearchTaskStatus(I18N::translate('Status')),
+ 'INDI:_TODO:TYPE' => new ResearchTaskType(I18N::translate('Type of research task')),
+ 'INDI:_TODO:_CAT' => new CustomElement(I18N::translate('Category')),
+ 'INDI:_TODO:_CDATE' => new DateValue(I18N::translate('Completion date')),
+ 'INDI:_TODO:_PRTY' => new ResearchTaskPriority(I18N::translate('Priority')),
+ 'INDI:_TODO:_RDATE' => new DateValue(I18N::translate('Reminder date')),
+ 'INDI:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'NOTE:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'OBJE:FILE:_PRIM' => new CustomElement(I18N::translate('Highlighted image')),
+ 'OBJE:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'REPO:ADDR:_NAME' => new CustomElement('Name of addressee'),
+ 'REPO:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'SOUR:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'SUBM:ADDR:_NAME' => new CustomElement('Name of addressee'),
+ 'SUBM:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ 'SUBN:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ '_LOC' => new LocationRecord(I18N::translate('Location')),
+ '_LOC:CHAN' => new Change(I18N::translate('Last change')),
+ '_LOC:CHAN:DATE' => new ChangeDate(I18N::translate('Date of last change')),
+ '_LOC:CHAN:DATE:TIME' => new TimeValue(I18N::translate('Time')),
+ '_LOC:CHAN:NOTE' => new NoteStructure(I18N::translate('Note')),
+ '_LOC:EVEN' => new EventDescriptor(I18N::translate('Event'), ['TYPE' => '0:1']),
+ '_LOC:EVEN:TYPE' => new EventAttributeType(I18N::translate('Type of event')),
+ '_LOC:MAP' => new EmptyElement(I18N::translate('Coordinates')),
+ '_LOC:MAP:LATI' => new PlaceLatitude(I18N::translate('Latitude')),
+ '_LOC:MAP:LONG' => new PlaceLongtitude(I18N::translate('Longitude')),
+ '_LOC:NAME' => new PlaceName(I18N::translate('Place'), ['ABBR' => '0:1', 'DATE' => '0:1', 'LANG' => '0:1', 'SOUR' => '0:M']),
+ '_LOC:NAME:ABBR' => new CustomElement(I18N::translate('Abbreviation')),
+ '_LOC:NAME:ABBR:TYPE' => new CustomElement(I18N::translate('Type of abbreviation')),
+ '_LOC:NAME:DATE' => new DateValue(I18N::translate('Date')),
+ '_LOC:NAME:LANG' => new LanguageId(I18N::translate('Language')),
+ '_LOC:NAME:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:NOTE' => new NoteStructure(I18N::translate('Note')),
+ '_LOC:OBJE' => new XrefMedia(I18N::translate('Media')),
+ '_LOC:RELI' => new ReligiousAffiliation('Religion', []),
+ '_LOC:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:SOUR:DATA' => new SourceData(I18N::translate('Data')),
+ '_LOC:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
+ '_LOC:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
+ '_LOC:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
+ '_LOC:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
+ '_LOC:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
+ '_LOC:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
+ '_LOC:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
+ '_LOC:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ '_LOC:TYPE' => new CustomElement(I18N::translate('Type of location')),
+ '_LOC:TYPE:DATE' => new DateValue(I18N::translate('Date')),
+ '_LOC:TYPE:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:TYPE:_GOVTYPE' => new CustomElement('GOV identifier type'),
+ '_LOC:_AIDN' => new CustomElement('Administrative ID'),
+ '_LOC:_AIDN:DATE' => new DateValue(I18N::translate('Date')),
+ '_LOC:_AIDN:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:_AIDN:TYPE' => new CustomElement(I18N::translate('Type of administrative ID')),
+ '_LOC:_DMGD' => new CustomElement('Demographic data'),
+ '_LOC:_DMGD:DATE' => new DateValue(I18N::translate('Date')),
+ '_LOC:_DMGD:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:_DMGD:TYPE' => new CustomElement(I18N::translate('Type of demographic data')),
+ '_LOC:_GOV' => new GovIdentifier(I18N::translate('GOV identifier')),
+ '_LOC:_LOC' => new XrefLocation(I18N::translate('Parent'), ['DATE' => '0:1', 'SOUR' => '0:M', 'TYPE' => '0:1']),
+ '_LOC:_LOC:DATE' => new DateValue(I18N::translate('Date')),
+ '_LOC:_LOC:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:_LOC:TYPE' => new HierarchicalRelationship(I18N::translate('Hierarchical relationship')),
+ '_LOC:_MAIDENHEAD' => new MaidenheadLocator('Maidenhead locator'),
+ '_LOC:_POST' => new AddressPostalCode(I18N::translate('Postal code')),
+ '_LOC:_POST:DATE' => new DateValue(I18N::translate('Date')),
+ '_LOC:_POST:SOUR' => new XrefSource(I18N::translate('Source')),
+ '_LOC:_UID' => new PafUid(I18N::translate('Unique identifier')),
+ '_LOC:*:SOUR:DATA' => new SourceData(I18N::translate('Data')),
+ '_LOC:*:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
+ '_LOC:*:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
+ '_LOC:*:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
+ '_LOC:*:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
+ '_LOC:*:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
+ '_LOC:*:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
+ '_LOC:*:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
+ '_LOC:*:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'GEDCOM-L';
+ }
+}
diff --git a/app/Module/CustomTagsLegacy.php b/app/Module/CustomTagsLegacy.php
new file mode 100644
index 0000000000..b657d38291
--- /dev/null
+++ b/app/Module/CustomTagsLegacy.php
@@ -0,0 +1,138 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CustomElement;
+
+/**
+ * Class CustomTagsLegacy
+ */
+class CustomTagsLegacy extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @see http://support.legacyfamilytree.com/article/AA-00520/0/GEDCOM-Files-custom-tags-in-Legacy.html
+ *
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:*:ADDR:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
+ 'FAM:*:PLAC:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
+ 'FAM:*:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
+ 'FAM:*:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
+ 'FAM:CHIL:_FREL' => new CustomElement('The Relationship of a child to the Father (under a CHIL block under a FAM record).'),
+ 'FAM:CHIL:_MREL' => new CustomElement('The Relationship of a child to the Mother (under a CHIL block under a FAM record).'),
+ 'FAM:CHIL:_STAT' => new CustomElement('The Status of a marriage (Married, Unmarried, etc.). Also the Status of a child (Twin, Triplet, etc.). (The marriage status of Divorced is exported using a DIV tag.)'),
+ 'FAM:EVEN:_OVER' => new CustomElement('An event sentence override (under an EVEN block).'),
+ 'FAM:MARR:_STAT' => new CustomElement('The Status of a marriage (Married, Unmarried, etc.). Also the Status of a child (Twin, Triplet, etc.). (The marriage status of Divorced is exported using a DIV tag.)'),
+ 'FAM:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
+ 'FAM:_NONE' => new CustomElement('Indicates that a couple had no children (under a FAM record).'),
+ 'HEAD:_EVENT_DEFN' => new CustomElement('Indicates the start of an Event Definition record that describes the attributes of an event or fact.'),
+ 'HEAD:_EVENT_DEFN:_CONF_FLAG' => new CustomElement('Indicates that an event is Confidential or Private (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_DATE_TYPE' => new CustomElement('Indicates whether or not a Date field is shown for a specific event (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_DESC_FLAG' => new CustomElement('Indicates whether or not a Description field is shown for a specific event (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_PLACE_TYPE' => new CustomElement('Indicates whether or not a Place field is shown for a specific event (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_PP_EXCLUDE' => new CustomElement('Indicates that an event is to be Excluded from the Potential Problems reporting (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN1' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN2' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN3' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN4' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN5' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN6' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN7' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SEN8' => new CustomElement('Event sentence definitions (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENDOF' => new CustomElement('Event sentence for PAF5 if only the Date field is filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENDOM' => new CustomElement('Event sentence for PAF5 if only the Date field is filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENDOU' => new CustomElement('Event sentence for PAF5 if only the Date field is filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENDPF' => new CustomElement('Event sentence for PAF5 if only the Date and Place fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENDPM' => new CustomElement('Event sentence for PAF5 if only the Date and Place fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENDPU' => new CustomElement('Event sentence for PAF5 if only the Date and Place fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENF' => new CustomElement('Event sentence for PAF5 if all fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENM' => new CustomElement('Event sentence for PAF5 if all fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENPOF' => new CustomElement('Event sentence for PAF5 if only the Place field is filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENPOM' => new CustomElement('Event sentence for PAF5 if only the Place field is filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENPOU' => new CustomElement('Event sentence for PAF5 if only the Place field is filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_EVENT_DEFN:_SENU' => new CustomElement('Event sentence for PAF5 if all fields are filled in for a Male individual (under an _EVENT_DEFN record).'),
+ 'HEAD:_PLAC_DEFN' => new CustomElement('Indicates the start of a Place Definition record that describes the attribute of a place.'),
+ 'HEAD:_PLAC_DEFN:_PREP' => new CustomElement('A location Preposition (under a _PLAC_DEFN record).'),
+ 'INDI:*:ADDR:_LIST3 YES' => new CustomElement('Indicates that a person’s address is part of the Birthday grouping (under an ADDR block).'),
+ 'INDI:*:ADDR:_LIST4 YES' => new CustomElement('Indicates that a person’s address is part of the Research grouping (under an ADDR block).'),
+ 'INDI:*:ADDR:_LIST5 YES' => new CustomElement('Indicates that a person’s address is part of the Christmas grouping (under an ADDR block).'),
+ 'INDI:*:ADDR:_LIST6 YES' => new CustomElement('Indicates that a person’s address is part of the Holiday grouping (under an ADDR block).'),
+ 'INDI:*:ADDR:_NAME' => new CustomElement('The name of an individual as part of an address (under an ADDR block).'),
+ 'INDI:*:ADDR:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
+ 'INDI:*:ADDR:_SORT' => new CustomElement('The spelling of a name to be used when sorting addresses for a report (under an ADDR block).'),
+ 'INDI:*:ADDR:_TAG' => new CustomElement('Indicates that an address, or place has been tagged. Also used for Tag 1 selection for an individual.'),
+ 'INDI:*:PLAC:_TAG' => new CustomElement('Indicates that an address, or place has been tagged. Also used for Tag 1 selection for an individual.'),
+ 'INDI:*:PLAC:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
+ 'INDI:*:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
+ 'INDI:*:_PRIV' => new CustomElement('Indicates that an address or event is marked as Private.'),
+ 'INDI:ADDR:_EMAIL' => new CustomElement('An email address (under an ADDR block).'),
+ 'INDI:ADDR:_LIST1 YES' => new CustomElement('Indicates that a person’s address is part of the Newsletter grouping (under an ADDR block).'),
+ 'INDI:ADDR:_LIST2 YES' => new CustomElement('Indicates that a person’s address is part of the Family Association grouping (under an ADDR block).'),
+ 'INDI:EVEN:_OVER' => new CustomElement('An event sentence override (under an EVEN block).'),
+ 'INDI:SOUR:_VERI' => new CustomElement('Indicates that a source citation or place name has a checkmark in the Verified column.'),
+ 'INDI:_TAG' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG2' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG3' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG4' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG5' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG6' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG7' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG8' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TAG9' => new CustomElement('When under an INDI record, indicates that an individual has been given certain tag marks.'),
+ 'INDI:_TODO' => new CustomElement('Research task'),
+ 'INDI:_TODO:_CAT' => new CustomElement('The Category of a To-Do item (under a _TODO record).'),
+ 'INDI:_TODO:_CDATE' => new CustomElement('Closed Date of a To-Do item (under a _TODO record).'),
+ 'INDI:_TODO:_LOCL' => new CustomElement('The Locality of a To-Do item (under a _TODO record).'),
+ 'INDI:_TODO:_RDATE' => new CustomElement('Reminder date on to-do items. (Under a _TODO record.)'),
+ 'INDI:_UID' => new CustomElement('A Unique Identification Number given to each individual in a family file.'),
+ 'INDI:_URL' => new CustomElement('An Internet address (under an INDI record).'),
+ 'OBJE:_DATE' => new CustomElement('A date associated with a multimedia object, usually a picture or video (under an OBJE block).'),
+ 'OBJE:_PRIM' => new CustomElement('Means a multimedia object, usually a picture, is the Primary object (the one that is shown on a report) (under an OBJE block).'),
+ 'OBJE:_SCBK' => new CustomElement('Indicates that a Picture is tagged to be included in a scrapbook report (under an OBJE block).'),
+ 'OBJE:_SOUND' => new CustomElement('A sound file name that is attached to a picture (under an OBJE block).'),
+ 'OBJE:_TYPE' => new CustomElement('The type of a multimedia object: Photo, Sound, or Video (under an OBJE block).'),
+ 'SOUR:_ITALIC Y' => new CustomElement('Indicates that a source title should be printed on a report in italics (under a SOUR record).'),
+ 'SOUR:_PAREN' => new CustomElement('Indicates that the Publication Facts of a source should be printed within parentheses on a report (under a SOUR record).'),
+ 'SOUR:_QUOTED Y' => new CustomElement('Indicates that a source title should be printed within quotes on a report (under a SOUR record).'),
+ 'SOUR:_TAG NO' => new CustomElement('When used under a SOUR record, indicates to exclude the source citation detail on reports.'),
+ 'SOUR:_TAG2 NO' => new CustomElement('When used under a SOUR record, indicates to exclude the source citation on reports.'),
+ 'SOUR:_TAG3 YES' => new CustomElement('When used under a SOUR record, indicates to include the source citation detail text on reports.'),
+ 'SOUR:_TAG4 YES' => new CustomElement('When used under a SOUR record, indicates to include the source citation detail notes on reports.'),
+ '_PREF' => new CustomElement('Indicates a Preferred spouse, child or parents.'), // How is this used?
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Legacy™';
+ }
+}
diff --git a/app/Module/CustomTagsPersonalAncestralFile.php b/app/Module/CustomTagsPersonalAncestralFile.php
new file mode 100644
index 0000000000..28827c3f39
--- /dev/null
+++ b/app/Module/CustomTagsPersonalAncestralFile.php
@@ -0,0 +1,67 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\AddressEmail;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\NamePersonal;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsPersonalAncestralFile
+ */
+class CustomTagsPersonalAncestralFile extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ *
+ * @see http://wiki-de.genealogy.net/GEDCOM/_Nutzerdef-Tag
+ */
+ public function customTags(): array
+ {
+ return [
+ 'INDI:NAME:_ADPN' => new NamePersonal(I18N::translate('Adopted name'), []),
+ 'INDI:NAME:_AKA' => new NamePersonal(I18N::translate('Also known as'), []),
+ 'INDI:NAME:_AKAN' => new NamePersonal(I18N::translate('Also known as'), []),
+ 'INDI:_EMAIL' => new AddressEmail(I18N::translate('Email address')),
+ 'URL' => new CustomElement(I18N::translate('URL')),
+ '_HEB' => new CustomElement(I18N::translate('Hebrew')),
+ '_NAME' => new CustomElement(I18N::translate('Mailing name')),
+ '_SCBK' => new CustomElement(I18N::translate('Scrapbook')),
+ '_SSHOW' => new CustomElement(I18N::translate('Slide show')),
+ '_TYPE' => new CustomElement(I18N::translate('Media type')),
+ '_URL' => new CustomElement(I18N::translate('URL')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Personal Ancestral File™';
+ }
+}
diff --git a/app/Module/CustomTagsPhpGedView.php b/app/Module/CustomTagsPhpGedView.php
new file mode 100644
index 0000000000..e71f38ae3f
--- /dev/null
+++ b/app/Module/CustomTagsPhpGedView.php
@@ -0,0 +1,95 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\AddressEmail;
+use Fisharebest\Webtrees\Elements\AddressLine;
+use Fisharebest\Webtrees\Elements\AddressWebPage;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\Elements\CustomEvent;
+use Fisharebest\Webtrees\Elements\NamePersonal;
+use Fisharebest\Webtrees\Elements\NoteStructure;
+use Fisharebest\Webtrees\Elements\RelationIsDescriptor;
+use Fisharebest\Webtrees\Elements\RestrictionNotice;
+use Fisharebest\Webtrees\Elements\TimeValue;
+use Fisharebest\Webtrees\Elements\WebtreesUser;
+use Fisharebest\Webtrees\Elements\XrefAssociate;
+use Fisharebest\Webtrees\Elements\XrefMedia;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsPhpGedView
+ */
+class CustomTagsPhpGedView extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'FAM:COMM' => new CustomElement(I18N::translate('Comment')),
+ 'INDI:*:ASSO' => new XrefAssociate(I18N::translate('Associate')),
+ 'INDI:*:ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
+ 'INDI:*:PLAC:_HEB' => new NoteStructure(I18N::translate('Place in Hebrew')),
+ 'INDI:ADDR' => new AddressLine(I18N::translate('Address')),
+ 'INDI:BIRT:DATE:TIME' => new TimeValue(I18N::translate('Time')),
+ 'INDI:BURI:CEME' => new CustomElement(I18N::translate('Cemetery')),
+ 'INDI:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'INDI:COMM' => new CustomElement(I18N::translate('Comment')),
+ 'INDI:DEAT:DATE:TIME' => new TimeValue(I18N::translate('Time')),
+ 'INDI:EMAIL' => new AddressEmail(I18N::translate('Email address')),
+ 'INDI:NAME:_HEB' => new NamePersonal(I18N::translate('Name in Hebrew'), []),
+ 'INDI:_FNRL' => new CustomEvent(I18N::translate('Funeral')),
+ 'INDI:_HOL' => new CustomEvent(I18N::translate('Holocaust')),
+ 'INDI:_MILI' => new CustomEvent(I18N::translate('Military')),
+ 'INDI:_PGV_OBJS' => new XrefMedia(I18N::translate('Re-order media')),
+ 'NOTE:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'OBJE:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'OBJE:_PRIM' => new CustomElement(I18N::translate('Highlighted image')),
+ 'OBJE:_THUM' => new CustomElement(I18N::translate('Thumbnail image')),
+ 'REPO:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'SOUR:CHAN:_PGVU' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'SOUR:SERV' => new CustomElement(I18N::translate('Remote server')),
+ 'SOUR:URL' => new AddressWebPage(I18N::translate('URL')),
+ 'SOUR:URL:TYPE' => new CustomElement(I18N::translate('Type')), // e.g. "FamilySearch"
+ 'SOUR:URL:_BLOCK' => new CustomElement(I18N::translate('Block')), // "e.g. "false"
+ 'SOUR:_DBID' => new CustomElement(I18N::translate('Database name')),
+ 'SOUR:_DBID:_PASS' => new CustomElement(I18N::translate('Database password')),
+ 'SOUR:_DBID:_PASS:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ 'SOUR:_DBID:_USER' => new CustomElement(I18N::translate('Database user account')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'PhpGedView™';
+ }
+}
diff --git a/app/Module/CustomTagsReunion.php b/app/Module/CustomTagsReunion.php
new file mode 100644
index 0000000000..355ec70693
--- /dev/null
+++ b/app/Module/CustomTagsReunion.php
@@ -0,0 +1,59 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\AddressEmail;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsReunion
+ */
+class CustomTagsReunion extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'INDI:EMAL' => new AddressEmail(I18N::translate('Email address')),
+ 'INDI:CITN' => new CustomElement(I18N::translate('Citizenship')),
+ 'INDI:_LEGA' => new CustomElement(I18N::translate('Legatee')),
+ 'INDI:_MDCL' => new CustomElement(I18N::translate('Medical')),
+ 'INDI:_PURC' => new CustomElement('Land purchase'),
+ 'INDI:_SALE' => new CustomElement('Land sale'),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Reunion™';
+ }
+}
diff --git a/app/Module/CustomTagsRootsMagic.php b/app/Module/CustomTagsRootsMagic.php
new file mode 100644
index 0000000000..cbc96d0219
--- /dev/null
+++ b/app/Module/CustomTagsRootsMagic.php
@@ -0,0 +1,59 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\AddressWebPage;
+use Fisharebest\Webtrees\Elements\CustomElement;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsRootsMagic
+ */
+class CustomTagsRootsMagic extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'INDI:_DNA' => new CustomElement(I18N::translate('DNA markers')),
+ 'INDI:_WEBTAG' => new CustomElement(I18N::translate('External link')),
+ 'INDI:_WEBTAG:NAME' => new CustomElement(I18N::translate('Text')),
+ 'INDI:_WEBTAG:URL' => new AddressWebPage(I18N::translate('URL')),
+ 'SOUR:_BIBL' => new CustomElement(I18N::translate('Bibliography')),
+ 'SOUR:_SUBQ' => new CustomElement(I18N::translate('Abbreviation')),
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'Roots Magic™';
+ }
+}
diff --git a/app/Module/CustomTagsWebtrees.php b/app/Module/CustomTagsWebtrees.php
new file mode 100644
index 0000000000..7c98982690
--- /dev/null
+++ b/app/Module/CustomTagsWebtrees.php
@@ -0,0 +1,124 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Elements\CertaintyAssessment;
+use Fisharebest\Webtrees\Elements\EntryRecordingDate;
+use Fisharebest\Webtrees\Elements\EventTypeCitedFrom;
+use Fisharebest\Webtrees\Elements\NoteStructure;
+use Fisharebest\Webtrees\Elements\RelationIsDescriptor;
+use Fisharebest\Webtrees\Elements\RestrictionNotice;
+use Fisharebest\Webtrees\Elements\RoleInEvent;
+use Fisharebest\Webtrees\Elements\SourceData;
+use Fisharebest\Webtrees\Elements\TextFromSource;
+use Fisharebest\Webtrees\Elements\WebtreesUser;
+use Fisharebest\Webtrees\Elements\WhereWithinSource;
+use Fisharebest\Webtrees\Elements\XrefAssociate;
+use Fisharebest\Webtrees\Elements\XrefMedia;
+use Fisharebest\Webtrees\Elements\XrefSource;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * Class CustomTagsWebtrees
+ */
+class CustomTagsWebtrees extends AbstractModule implements ModuleConfigInterface, ModuleCustomTagsInterface
+{
+ use ModuleConfigTrait;
+ use ModuleCustomTagsTrait;
+
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array
+ {
+ return [
+ 'FAM:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'FAM:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
+ 'FAM:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
+ 'FAM:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
+ 'FAM:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'FAM:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
+ 'FAM:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
+ 'FAM:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
+ 'FAM:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
+ 'FAM:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
+ 'FAM:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
+ 'FAM:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
+ 'FAM:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'FAM:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
+ 'FAM:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
+ 'FAM:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'INDI:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'INDI:*:_ASSO' => new XrefAssociate(I18N::translate('Associate')),
+ 'INDI:*:_ASSO:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'INDI:*:_ASSO:RELA' => new RelationIsDescriptor(I18N::translate('Relationship')),
+ 'INDI:*:_ASSO:SOUR' => new XrefSource(I18N::translate('Source citation')),
+ 'INDI:*:_ASSO:SOUR:DATA' => new SourceData(I18N::translate('Data')),
+ 'INDI:*:_ASSO:SOUR:DATA:DATE' => new EntryRecordingDate(I18N::translate('Date of entry in original source')),
+ 'INDI:*:_ASSO:SOUR:DATA:TEXT' => new TextFromSource(I18N::translate('Text')),
+ 'INDI:*:_ASSO:SOUR:EVEN' => new EventTypeCitedFrom(I18N::translate('Event')),
+ 'INDI:*:_ASSO:SOUR:EVEN:ROLE' => new RoleInEvent(I18N::translate('Role')),
+ 'INDI:*:_ASSO:SOUR:NOTE' => new NoteStructure(I18N::translate('Note')),
+ 'INDI:*:_ASSO:SOUR:OBJE' => new XrefMedia(I18N::translate('Media object')),
+ 'INDI:*:_ASSO:SOUR:PAGE' => new WhereWithinSource(I18N::translate('Citation details')),
+ 'INDI:*:_ASSO:SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
+ 'NOTE:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'NOTE:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ 'OBJE:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'OBJE:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ 'REPO:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'REPO:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ 'SOUR:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'SOUR:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ 'SUBM:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ 'SUBM:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ '_LOC:CHAN:_WT_USER' => new WebtreesUser(I18N::translate('Author of last change')),
+ '_LOC:RESN' => new RestrictionNotice(I18N::translate('Restriction')),
+ ];
+ }
+
+ /**
+ * @return array<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', ''],
+ ];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return 'webtrees™';
+ }
+}
diff --git a/app/Module/ModuleCustomTagsInterface.php b/app/Module/ModuleCustomTagsInterface.php
new file mode 100644
index 0000000000..36593bf7cf
--- /dev/null
+++ b/app/Module/ModuleCustomTagsInterface.php
@@ -0,0 +1,45 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+
+/**
+ * Interface ModuleCustomTagsInterface
+ */
+interface ModuleCustomTagsInterface extends ModuleInterface
+{
+ /**
+ * @return array<string,ElementInterface>
+ */
+ public function customTags(): array;
+
+ /**
+ * @return array<array<string>>
+ */
+ public function customSubTags(): array;
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string;
+}
diff --git a/app/Module/ModuleCustomTagsTrait.php b/app/Module/ModuleCustomTagsTrait.php
new file mode 100644
index 0000000000..1c8b44bd97
--- /dev/null
+++ b/app/Module/ModuleCustomTagsTrait.php
@@ -0,0 +1,113 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Http\ViewResponseTrait;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Registry;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+
+/**
+ * Trait ModuleCustomTagsTrait
+ */
+trait ModuleCustomTagsTrait
+{
+ use ViewResponseTrait;
+
+ public function boot(): void
+ {
+ $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);
+ }
+ }
+
+ /**
+ * 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>
+ */
+ public function customTags(): array
+ {
+ return [];
+ }
+
+ /**
+ * @return array<array<string>>
+ */
+ public function customSubTags(): array
+ {
+ return [];
+ }
+
+ /**
+ * The application for which we are supporting custom tags.
+ *
+ * @return string
+ */
+ public function customTagApplication(): string
+ {
+ return '';
+ }
+
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function getAdminAction(ServerRequestInterface $request): ResponseInterface
+ {
+ $this->layout = 'layouts/administration';
+
+ return $this->viewResponse('modules/custom-tags/config', [
+ 'tags' => $this->customTags(),
+ 'title' => $this->title(),
+ ]);
+ }
+}
diff --git a/app/Services/ModuleService.php b/app/Services/ModuleService.php
index d042c0c4c1..259c9794f5 100644
--- a/app/Services/ModuleService.php
+++ b/app/Services/ModuleService.php
@@ -49,6 +49,19 @@ use Fisharebest\Webtrees\Module\ColorsTheme;
use Fisharebest\Webtrees\Module\CompactTreeChartModule;
use Fisharebest\Webtrees\Module\ContactsFooterModule;
use Fisharebest\Webtrees\Module\CustomCssJsModule;
+use Fisharebest\Webtrees\Module\CustomTagsAldfaer;
+use Fisharebest\Webtrees\Module\CustomTagsBrothersKeeper;
+use Fisharebest\Webtrees\Module\CustomTagsFamilyTreeBuilder;
+use Fisharebest\Webtrees\Module\CustomTagsFamilyTreeMaker;
+use Fisharebest\Webtrees\Module\CustomTagsGedcom53;
+use Fisharebest\Webtrees\Module\CustomTagsGedcom55;
+use Fisharebest\Webtrees\Module\CustomTagsGedcomL;
+use Fisharebest\Webtrees\Module\CustomTagsLegacy;
+use Fisharebest\Webtrees\Module\CustomTagsPersonalAncestralFile;
+use Fisharebest\Webtrees\Module\CustomTagsPhpGedView;
+use Fisharebest\Webtrees\Module\CustomTagsReunion;
+use Fisharebest\Webtrees\Module\CustomTagsRootsMagic;
+use Fisharebest\Webtrees\Module\CustomTagsWebtrees;
use Fisharebest\Webtrees\Module\CzechMonarchsAndPresidents;
use Fisharebest\Webtrees\Module\DeathReportModule;
use Fisharebest\Webtrees\Module\DescendancyChartModule;
@@ -188,6 +201,7 @@ use Fisharebest\Webtrees\Module\ModuleAnalyticsInterface;
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleCustomInterface;
+use Fisharebest\Webtrees\Module\ModuleCustomTagsInterface;
use Fisharebest\Webtrees\Module\ModuleDataFixInterface;
use Fisharebest\Webtrees\Module\ModuleFooterInterface;
use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
@@ -273,6 +287,7 @@ class ModuleService
ModuleAnalyticsInterface::class,
ModuleBlockInterface::class,
ModuleChartInterface::class,
+ ModuleCustomTagsInterface::class,
ModuleDataFixInterface::class,
ModuleFooterInterface::class,
ModuleHistoricEventsInterface::class,
@@ -337,6 +352,19 @@ class ModuleService
'contact-links' => ContactsFooterModule::class,
'czech-leaders' => CzechMonarchsAndPresidents::class,
'custom-css-js' => CustomCssJsModule::class,
+ 'custom-tags-aldfaer' => CustomTagsAldfaer::class,
+ 'custom-tags-bk' => CustomTagsBrothersKeeper::class,
+ 'custom-tags-gedcom-53' => CustomTagsGedcom53::class,
+ 'custom-tags-gedcom-55' => CustomTagsGedcom55::class,
+ 'custom-tags-gedcom-l' => CustomTagsGedcomL::class,
+ 'custom-tags-legacy' => CustomTagsLegacy::class,
+ 'custom-tags-ftb' => CustomTagsFamilyTreeBuilder::class,
+ 'custom-tags-ftm' => CustomTagsFamilyTreeMaker::class,
+ 'custom-tags-paf' => CustomTagsPersonalAncestralFile::class,
+ 'custom-tags-phpgedview' => CustomTagsPhpGedView::class,
+ 'custom-tags-reunion' => CustomTagsReunion::class,
+ 'custom-tags-roots-magic' => CustomTagsRootsMagic::class,
+ 'custom-tags-webtrees' => CustomTagsWebtrees::class,
'death_report' => DeathReportModule::class,
'descendancy' => DescendancyModule::class,
'descendancy_chart' => DescendancyChartModule::class,
diff --git a/resources/views/admin/control-panel.phtml b/resources/views/admin/control-panel.phtml
index d36563d594..a36faf3b6e 100644
--- a/resources/views/admin/control-panel.phtml
+++ b/resources/views/admin/control-panel.phtml
@@ -15,6 +15,7 @@ use Fisharebest\Webtrees\Http\RequestHandlers\ModulesAllPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesAnalyticsPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesBlocksPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesChartsPage;
+use Fisharebest\Webtrees\Http\RequestHandlers\ModulesCustomTagsPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesDataFixesPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesFootersPage;
use Fisharebest\Webtrees\Http\RequestHandlers\ModulesHistoricEventsPage;
@@ -52,6 +53,7 @@ use Fisharebest\Webtrees\Module\ModuleBlockInterface;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
use Fisharebest\Webtrees\Module\ModuleCustomInterface;
+use Fisharebest\Webtrees\Module\ModuleCustomTagsInterface;
use Fisharebest\Webtrees\Module\ModuleDataFixInterface;
use Fisharebest\Webtrees\Module\ModuleFooterInterface;
use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
@@ -91,6 +93,8 @@ use Illuminate\Support\Collection;
* @var Collection<ModuleChartInterface> $chart_modules_disabled
* @var Collection<ModuleChartInterface> $chart_modules_enabled
* @var Collection<ModuleCustomInterface> $custom_updates
+* @var Collection<ModuleCustomTagsInterface> $custom_tags_modules_disabled
+ * @var Collection<ModuleCustomTagsInterface> $custom_tags_modules_enabled
* @var Collection<ModuleDataFixInterface> $data_fix_modules_disabled
* @var Collection<ModuleDataFixInterface> $data_fix_modules_enabled
* @var Collection<string,int> $families
@@ -684,6 +688,13 @@ use Illuminate\Support\Collection;
</a>
<?= view('components/badge', ['count' => $data_fix_modules_enabled->count(), 'total' => $data_fix_modules_disabled->count(), 'context' => 'primary']) ?>
</li>
+ <li>
+ <span class="fa-li"><?= view('icons/tag') ?></span>
+ <a href="<?= e(route(ModulesCustomTagsPage::class)) ?>">
+ <?= I18N::translate('Custom tags') ?>
+ </a>
+ <?= view('components/badge', ['count' => $custom_tags_modules_enabled->count(), 'total' => $custom_tags_modules_disabled->count(), 'context' => 'primary']) ?>
+ </li>
</ul>
<h3>
diff --git a/resources/views/edit/edit-fact.phtml b/resources/views/edit/edit-fact.phtml
index c6339bcef6..fc1350343c 100644
--- a/resources/views/edit/edit-fact.phtml
+++ b/resources/views/edit/edit-fact.phtml
@@ -106,24 +106,13 @@ use Ramsey\Uuid\Uuid;
?>
<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')]) ?>
- <div>
- <?= I18N::translate('%1$s: %2$s', I18N::translate('Timestamp'), view('components/datetime', ['timestamp' => $fact->record()->lastChangeTimestamp()])) ?>
- </div>
- <div>
- <?= I18N::translate('%1$s: %2$s', I18N::translate('Author of last change'), e($fact->record()->lastChangeUser())) ?>
- </div>
- </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">
+ <div class="form-group">
+ <?= view('components/checkbox', ['label' => I18N::translate('Keep the existing “last change” information'), 'name' => 'keep_chan', 'checked' => (bool) $tree->getPreference('NO_UPDATE_CHAN')]) ?>
+ </div>
+
<button class="btn btn-primary" type="submit">
<?= view('icons/save') ?>
<?= /* I18N: A button label. */
diff --git a/resources/views/edit/edit-gedcom-fields.phtml b/resources/views/edit/edit-gedcom-fields.phtml
index 4c6fafbb07..1e58a51d3a 100644
--- a/resources/views/edit/edit-gedcom-fields.phtml
+++ b/resources/views/edit/edit-gedcom-fields.phtml
@@ -12,6 +12,7 @@ use Ramsey\Uuid\Uuid;
*/
$gedcom = preg_replace('/\n\d CONT ?/', "\r", $gedcom);
+
?>
<?php foreach (explode("\n", $gedcom) as $line) : ?>
@@ -26,6 +27,7 @@ $gedcom = preg_replace('/\n\d CONT ?/', "\r", $gedcom);
<div class="form-group row">
<label class="col-sm-3 col-form-label" for="<?= e($id) ?>">
+ <?= str_repeat('&nbsp;', max(0, $level -1)) ?>
<?= $element->label() ?>
</label>
diff --git a/resources/views/edit/edit-record.phtml b/resources/views/edit/edit-record.phtml
index 7070641203..9a6fa767f2 100644
--- a/resources/views/edit/edit-record.phtml
+++ b/resources/views/edit/edit-record.phtml
@@ -23,27 +23,16 @@ use Fisharebest\Webtrees\Tree;
<?= view('edit/edit-gedcom-fields', ['gedcom' => $record->insertMissingSubtags(), 'hierarchy' => [$record->tag()], 'tree' => $tree, 'prefix' => '']) ?>
- <?php if ($has_chan): ?>
- <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')]) ?>
- <div>
- <?= I18N::translate('%1$s: %2$s', I18N::translate('Timestamp'), view('components/datetime', ['timestamp' => $record->lastChangeTimestamp()])) ?>
- </div>
- <div>
- <?= I18N::translate('%1$s: %2$s', I18N::translate('Author of last change'), e($record->lastChangeUser())) ?>
- </div>
- </div>
- </div>
- <?php endif ?>
-
<div class="form-group row">
<div class="col-sm-3 wt-page-options-label">
</div>
<div class="col-sm-9 wt-page-options-value">
+ <?php if ($has_chan): ?>
+ <div class="form-group">
+ <?= view('components/checkbox', ['label' => I18N::translate('Keep the existing “last change” information'), 'name' => 'keep_chan', 'checked' => (bool) $tree->getPreference('NO_UPDATE_CHAN')]) ?>
+ </div>
+ <?php endif ?>
+
<button class="btn btn-primary" type="submit">
<?= view('icons/save') ?>
<?= /* I18N: A button label. */
diff --git a/resources/views/modules/custom-tags/config.phtml b/resources/views/modules/custom-tags/config.phtml
new file mode 100644
index 0000000000..905a75198f
--- /dev/null
+++ b/resources/views/modules/custom-tags/config.phtml
@@ -0,0 +1,42 @@
+<?php
+
+use Fisharebest\Webtrees\Contracts\ElementInterface;
+use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel;
+use Fisharebest\Webtrees\Http\RequestHandlers\ModulesAllPage;
+use Fisharebest\Webtrees\I18N;
+
+/**
+ * @var array<string,ElementInterface> $tags
+ * @var string $title
+ */
+
+?>
+
+<?= view('components/breadcrumbs', ['links' => [route(ControlPanel::class) => I18N::translate('Control panel'), route(ModulesAllPage::class) => I18N::translate('Modules'), $title]]) ?>
+
+<h1><?= $title ?></h1>
+
+<table class="table table-bordered">
+ <caption class="sr-only"><?= I18N::translate('Custom tags') ?></caption>
+ <thead>
+ <tr>
+ <th><?= I18N::translate('Custom tag') ?></th>
+ <th><?= I18N::translate('Label') ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($tags as $tag => $element) : ?>
+ <tr>
+ <td><code><?= $tag ?></code></td>
+ <td><?= $element->label() ?></td>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+</table>
+
+<p>
+ <a href="<?= e(route(ControlPanel::class)) ?>" class="btn btn-primary">
+ <?= view('icons/save') ?>
+ <?= I18N::translate('continue') ?>
+ </a>
+</p>
diff --git a/resources/views/repository-page-menu.phtml b/resources/views/repository-page-menu.phtml
index d875725b57..014e450ef3 100644
--- a/resources/views/repository-page-menu.phtml
+++ b/resources/views/repository-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\I18N;
use Fisharebest\Webtrees\Repository;
@@ -19,16 +18,14 @@ use Fisharebest\Webtrees\Repository;
</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-confirm="<?= I18N::translate('Are you sure you want to delete “%s”?', strip_tags($record->fullName())) ?>" data-post-url="<?= e(route(DeleteRecord::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref()])) ?>">
<?= 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/source-page-menu.phtml b/resources/views/source-page-menu.phtml
index 60d58c9f2e..e6d32b41cf 100644
--- a/resources/views/source-page-menu.phtml
+++ b/resources/views/source-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\I18N;
use Fisharebest\Webtrees\Source;
@@ -19,17 +18,15 @@ use Fisharebest\Webtrees\Source;
</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-confirm="<?= I18N::translate('Are you sure you want to delete “%s”?', strip_tags($record->fullName())) ?>" data-post-url="<?= e(route(DeleteRecord::class, ['tree' => $record->tree()->name(), 'xref' => $record->xref()])) ?>">
<?= 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>