Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
83949fd
render DATE:_TIME the same as DATE:TIME
BertKoor Jul 9, 2025
88a3514
completed list of Heredis custom tags
BertKoor Jul 10, 2025
9335700
LATI/LONG tags swapped to their natural order
BertKoor Jul 10, 2025
164c009
moved some Heredis custom tags to Extra Information pane
BertKoor Jul 11, 2025
3456f00
custom subtags should extend AbstractElement, not AbstractEventElement
BertKoor Jul 11, 2025
53cbdfb
Heredis research information should be collapsable (wip)
BertKoor Jul 11, 2025
350bda9
use yes/no instead of Yes/No because this pair is in the same casing …
BertKoor Jul 14, 2025
f96603c
labels of Heredis tags changed to English, use I18N to translate back…
BertKoor Jul 14, 2025
c7e649c
moved Heredis 'etiquettes' (personal flags) to Extra Information pane
BertKoor Jul 14, 2025
5888032
codestyle: removed blank lines
BertKoor Jul 25, 2025
761f322
codecov: fixed missing tests
BertKoor Jul 25, 2025
4c6c4ac
codecov: increased test coverage
BertKoor Jul 25, 2025
04cee19
fixed phpstan issue in HeredisTest.php
BertKoor Jul 25, 2025
52f3010
translation context added for Heredis tag INDI:_FIL
BertKoor Dec 31, 2025
a2aa17d
removed param $sex on values(), seems never used
BertKoor Dec 31, 2025
d88df17
added translation context
BertKoor Dec 31, 2025
5884f9f
Heredis tag _FNA also occurs in Family records
BertKoor Feb 11, 2026
ce2b9aa
Heredis tag HEAD:_GUID is a regular UID, not PAF format
BertKoor Feb 13, 2026
5ad125e
Merge branch 'main' into heredis_customTags
BertKoor Feb 13, 2026
eb6d1e5
added Heredis tags HEAL & PAGE
BertKoor Feb 13, 2026
5dd0e76
hide tag FAM:_CREA on Individual facts & events
BertKoor Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 83 additions & 8 deletions app/CustomTags/Heredis.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,41 @@

use Fisharebest\Webtrees\Contracts\CustomTagInterface;
use Fisharebest\Webtrees\Contracts\ElementInterface;
use Fisharebest\Webtrees\Elements\AddressEmail;
use Fisharebest\Webtrees\Elements\AddressWebPage;
use Fisharebest\Webtrees\Elements\AgeAtEvent;
use Fisharebest\Webtrees\Elements\CertaintyAssessment;
use Fisharebest\Webtrees\Elements\Coordinates;
use Fisharebest\Webtrees\Elements\Creation;
use Fisharebest\Webtrees\Elements\CustomBooleanFact;
use Fisharebest\Webtrees\Elements\CustomElement;
use Fisharebest\Webtrees\Elements\DateValueExact;
use Fisharebest\Webtrees\Elements\EmptyElement;
use Fisharebest\Webtrees\Elements\HeredisFIL;
use Fisharebest\Webtrees\Elements\HeredisFNA;
use Fisharebest\Webtrees\Elements\HeredisQualEvid;
use Fisharebest\Webtrees\Elements\HeredisQualInfo;
use Fisharebest\Webtrees\Elements\HeredisQualSour;
use Fisharebest\Webtrees\Elements\HeredisRechElement;
use Fisharebest\Webtrees\Elements\HeredisUST;
use Fisharebest\Webtrees\Elements\NoteStructure;
use Fisharebest\Webtrees\Elements\PlaceLatitude;
use Fisharebest\Webtrees\Elements\PlaceLongtitude;
use Fisharebest\Webtrees\Elements\PlaceName;
use Fisharebest\Webtrees\Elements\RoleInEvent;
use Fisharebest\Webtrees\Elements\SourceCallNumber;
use Fisharebest\Webtrees\Elements\TimeValue;
use Fisharebest\Webtrees\Elements\Uid;
use Fisharebest\Webtrees\Elements\UserReferenceNumber;
use Fisharebest\Webtrees\I18N;

/**
* GEDCOM files created by Heredis
*
* @see https://www.heredis.com
*
* Reference: https://help.heredis.com/les-tags-gedcom/ and https://help.heredis.com/en/gedcom-tags/
* Example GEDCOM fragment with lots of custom tags: https://www.geneanet.org/forum/viewtopic.php?p=2441735
*/
class Heredis implements CustomTagInterface
{
Expand All @@ -51,18 +77,67 @@ public function name(): string
public function tags(): array
{
return [
'HEAD:_GUID' => new Uid(I18N::translate('File identifier')),
'FAM:_CREA' => new Creation(I18N::translate('Created at')),
'FAM:_UST' => new HeredisUST(I18N::translate('Union type')),
'FAM:*:DATE:_TIME' => new TimeValue(I18N::translate('Time')),
'FAM:*:PLAC:_SUBMAP' => new Coordinates(I18N::translate('Geolocation of subdivisions')),
'FAM:*:PLAC:_SUBMAP:LATI' => new PlaceLatitude(I18N::translate('Latitude of subdivision')),
'FAM:*:PLAC:_SUBMAP:LONG' => new PlaceLongtitude(I18N::translate('Longitude of subdivision')),
'FAM:*:SOUR:_QUAL' => new EmptyElement(I18N::translate('Quality of completed citation')),
'FAM:*:SOUR:_QUAL:_SOUR' => new HeredisQualSour(I18N::translate('Quality of the source')),
'FAM:*:SOUR:_QUAL:_INFO' => new HeredisQualInfo(I18N::translate('Quality of the information')),
'FAM:*:SOUR:_QUAL:_EVID' => new HeredisQualEvid(I18N::translate('Quality of the evidence')),
'FAM:*:_FNA' => new HeredisFNA(I18N::translate('Research status')),
'FAM:*:_RECH' => new EmptyElement(I18N::translate('Research data')),
'FAM:*:_RECH:_PROJ' => new CustomElement(I18N::translate('Research project')),
'FAM:*:_RECH:TYPE' => new CustomElement(I18N::translate('Document')),
'FAM:*:_RECH:PLAC' => new PlaceName(I18N::translate('Search place')),
'FAM:*:_RECH:DATE' => new DateValueExact(I18N::translate('Search date')),
'FAM:*:_RECH:REFN' => new UserReferenceNumber(I18N::translate('Call number')),
'FAM:*:_RECH:WWW' => new AddressWebPage(I18N::translate('URL')),
'FAM:*:_RECH:NOTE' => new NoteStructure(I18N::translate('Note')),
'INDI:_CLS' => new CustomBooleanFact(I18N::translate('Childless')),
'INDI:_CREA' => new Creation(I18N::translate('Created at')),
'INDI:SIGN' => new CustomElement(I18N::translate('Signature')),
'INDI:_FIL' => new HeredisFIL(I18N::translate('Child status')),
'INDI:_ETI' => new CustomElement(I18N::translate('Personalized flag')),
'INDI:_FNF' => new CustomBooleanFact(I18N::translate('Untraceable father')),
'INDI:_MNF' => new CustomBooleanFact(I18N::translate('Untraceable mother')),
'INDI:_SEC' => new CustomBooleanFact(I18N::translate('Secondary person')),
'INDI:_ULS' => new CustomBooleanFact(I18N::translate('Unmarried')),
'INDI:SIGN' => new CustomBooleanFact(I18N::translate('Signature')),
'INDI:ASSO:_AGE' => new AgeAtEvent(I18N::translate('Age')),
'INDI:ASSO:_ROLE' => new RoleInEvent(I18N::translate('Role')),
'INDI:ASSO:_TITL' => new CustomElement(I18N::translate('Title')),
'INDI:ASSO:_TYPE' => new CustomElement(I18N::translate('Type')),
'INDI:HEAL' => new CustomElement(I18N::translate('Health')),
'INDI:PAGE' => new CustomElement(I18N::translate('Reference number')),
'INDI:*:DATE:_TIME' => new TimeValue(I18N::translate('Time')),
'INDI:*:PLAC:_SUBMAP' => new Coordinates(I18N::translate('Geolocation of subdivisions')),
'INDI:*:PLAC:_SUBMAP:LATI' => new PlaceLatitude(I18N::translate('Latitude of subdivision')),
'INDI:*:PLAC:_SUBMAP:LONG' => new PlaceLongtitude(I18N::translate('Longitude of subdivision')),
'INDI:*:SOUR:_QUAL' => new EmptyElement(I18N::translate('Quality of completed citation')),
'INDI:*:SOUR:_QUAL:_SOUR' => new HeredisQualSour(I18N::translate('Quality of the source')),
'INDI:*:SOUR:_QUAL:_INFO' => new HeredisQualInfo(I18N::translate('Quality of the information')),
'INDI:*:SOUR:_QUAL:_EVID' => new HeredisQualEvid(I18N::translate('Quality of proof')),
'INDI:*:_ETI' => new CustomElement(I18N::translate('Personalized flag')),
'INDI:*:_FNA' => new HeredisFNA(I18N::translate('Research status')),
'INDI:*:_RECH' => new HeredisRechElement(I18N::translate('Research data')),
'INDI:*:_RECH:_PROJ' => new CustomElement(I18N::translate('Research project')),
'INDI:*:_RECH:TYPE' => new CustomElement(I18N::translate('Document')),
'INDI:*:_RECH:PLAC' => new PlaceName(I18N::translate('Search place')),
'INDI:*:_RECH:DATE' => new DateValueExact(I18N::translate('Search date')),
'INDI:*:_RECH:REFN' => new SourceCallNumber(I18N::translate('Call number')),
'INDI:*:_RECH:WWW' => new AddressWebPage(I18N::translate('URL')),
'INDI:*:_RECH:NOTE' => new NoteStructure(I18N::translate('Note')),
'NOTE:_CREA' => new Creation(I18N::translate('Created at')),
'OBJE:_CREA' => new Creation(I18N::translate('Created at')),
'REPO:_CREA' => new Creation(I18N::translate('Created at')),
'SOUR:EMAIL' => new AddressEmail(I18N::translate('Email address')),
'SOUR:QUAY' => new CertaintyAssessment(I18N::translate('Quality of data')),
'SOUR:TYPE' => new CustomElement(I18N::translate('Type')),
/* Reported on the forum - but what do they mean?
'INDI:_FIL' => new CustomElement(I18N::translate('???')),
'INDI:*:_FNA' => new CustomElement(I18N::translate('???')),
'INDI:????:????:_SUBMAP' => new EmptyElement(I18N::translate('Coordinates'), ['INDI' => '1:1', 'LONG' => '1:1']),
'INDI:????:????:_SUBMAP:LATI' => new PlaceLatitude(I18N::translate('Latitude')),
'INDI:????:????:_SUBMAP:LONG' => new PlaceLongtitude(I18N::translate('Longitude')),
*/
'SOUR:_ARCH' => new CustomElement(I18N::translate('Archive')),
'SOUR:_CREA' => new Creation(I18N::translate('Created at')),
];
}
}
61 changes: 61 additions & 0 deletions app/Elements/CustomBooleanFact.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

/**
* webtrees: online genealogy
* Copyright (C) 2025 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\Elements;

use Fisharebest\Webtrees\I18N;

/**
* CustomBooleanFact, leniently accepting values yes and no, true and false, 1 and 0, in uppercase or lowercase.
*/
class CustomBooleanFact extends AbstractEventElement
{
/**
* Convert a value to a canonical form.
*
* @param string $value
*
* @return string
*/
public function canonical(string $value): string
{
$value = strtoupper(parent::canonical($value));
if (in_array($value, ['YES', 'TRUE', '1'])) {
return 'Y';
}
if (in_array($value, ['NO', 'FALSE', '0'])) {
return 'N';
}

return $value;
}

/**
* A list of controlled values for this element
*
* @return array<int|string,string>
*/
public function values(): array
{
return [
'Y' => I18N::translate('yes'),
'N' => I18N::translate('no'),
];
}
}
2 changes: 1 addition & 1 deletion app/Elements/Graduation.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class Graduation extends AbstractEventElement
{
protected const array SUBTAGS = [
'TYPE' => '0:1:?',
'TYPE' => '0:1',
'DATE' => '0:1',
'AGE' => '0:1',
'PLAC' => '0:1',
Expand Down
48 changes: 48 additions & 0 deletions app/Elements/HeredisFIL.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* webtrees: online genealogy
* Copyright (C) 2025 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\Elements;

use Fisharebest\Webtrees\I18N;

/**
* Heredis custom tag INDI:_FIL - Child status
*/
class HeredisFIL extends AbstractElement
{
/**
* A list of controlled values for this element
*
* @return array<int|string,string>
*/
public function values(): array
{
return [
'LEGITIMATE_CHILD' => I18N::translate('Legitimate'),
'NATURAL_CHILD' => I18N::translate('Natural'),
'RECOGNIZED_CHILD' => I18N::translate('Legally recognised'),
'LEGITIMIZED_CHILD' => I18N::translate('Legitimated'),
'CHILD_FOUND' => I18N::translate('Foundling'),
'ADOPTED_CHILD' => I18N::translateContext('Pedigree', 'Adopted'),
'ADULTEROUS_CHILD' => I18N::translate('Illegitimate'),
'STILLBORN_CHILD' => I18N::translate('stillborn'),
'RELATIONSHIP_UNKNOW' => I18N::translate('Unknown'),
];
}
}
43 changes: 43 additions & 0 deletions app/Elements/HeredisFNA.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/**
* webtrees: online genealogy
* Copyright (C) 2025 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\Elements;

use Fisharebest\Webtrees\I18N;

/**
* Heredis custom tag INDI:*:_FNA - Research status
*/
class HeredisFNA extends AbstractElement
{
/**
* A list of controlled values for this element
*
* @return array<int|string,string>
*/
public function values(): array
{
return [
'NO' => I18N::translate('No search record'),
'YES' => I18N::translate('Search record'),
'NOT_FOUND' => I18N::translate('Untraceable record'),
'IN_PROGRESS' => I18N::translate('Search in progress'),
];
}
}
42 changes: 42 additions & 0 deletions app/Elements/HeredisQualEvid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/**
* webtrees: online genealogy
* Copyright (C) 2025 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\Elements;

use Fisharebest\Webtrees\I18N;

/**
* Heredis custom tag *:*:SOUR:_QUAL:_EVID - quality of the evidence
*/
class HeredisQualEvid extends AbstractElement
{
/**
* A list of controlled values for this element
*
* @return array<int|string,string>
*/
public function values(): array
{
return [
'D' => I18N::translate('Direct'),
'I' => I18N::translate('Indirect'),
'N' => I18N::translate('Negative'),
];
}
}
41 changes: 41 additions & 0 deletions app/Elements/HeredisQualInfo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/**
* webtrees: online genealogy
* Copyright (C) 2025 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\Elements;

use Fisharebest\Webtrees\I18N;

/**
* Heredis custom tag *:*:SOUR:_QUAL:_INFO - quality of the information
*/
class HeredisQualInfo extends AbstractElement
{
/**
* A list of controlled values for this element
*
* @return array<int|string,string>
*/
public function values(): array
{
return [
'P' => I18N::translate('Primary'),
'S' => I18N::translate('Secondary'),
];
}
}
Loading
Loading