Hide family link facts (FAMC/FAMS/HUSB/WIFE/CHIL) from Facts & Events tab

Override fact.phtml to add bocken-family-fact class on family link rows,
then hide via CSS. Language-agnostic since it matches GEDCOM tags, not
translated labels. Info remains available in Relatives tab and sidebar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 21:39:45 +01:00
parent 1531c075c4
commit 77f067f8bc
3 changed files with 173 additions and 0 deletions

View File

@@ -432,6 +432,7 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul
View::registerCustomView('::modules/place-hierarchy/list', $this->name() . '::modules/place-hierarchy/list'); View::registerCustomView('::modules/place-hierarchy/list', $this->name() . '::modules/place-hierarchy/list');
View::registerCustomView('::lists/individuals-table', $this->name() . '::lists/individuals-table'); View::registerCustomView('::lists/individuals-table', $this->name() . '::lists/individuals-table');
View::registerCustomView('::lists/families-table', $this->name() . '::lists/families-table'); View::registerCustomView('::lists/families-table', $this->name() . '::lists/families-table');
View::registerCustomView('::fact', $this->name() . '::fact');
View::registerCustomView('::individual-page-images', $this->name() . '::individual-page-images'); View::registerCustomView('::individual-page-images', $this->name() . '::individual-page-images');
View::registerCustomView('::individual-page-menu', $this->name() . '::individual-page-menu'); View::registerCustomView('::individual-page-menu', $this->name() . '::individual-page-menu');
View::registerCustomView('::modules/family_nav/sidebar-family', $this->name() . '::modules/family_nav/sidebar-family'); View::registerCustomView('::modules/family_nav/sidebar-family', $this->name() . '::modules/family_nav/sidebar-family');

View File

@@ -420,6 +420,11 @@
display: flex !important; display: flex !important;
} }
/* Hide family link facts (FAMC/FAMS/HUSB/WIFE/CHIL) — redundant with Relatives tab */
.wt-route-IndividualPage .wt-facts-table > tbody > tr.bocken-family-fact {
display: none !important;
}
/* "Add a fact" row — flatten */ /* "Add a fact" row — flatten */
.wt-route-IndividualPage .wt-facts-table > tbody > tr:last-child { .wt-route-IndividualPage .wt-facts-table > tbody > tr:last-child {
background: transparent; background: transparent;

167
resources/views/fact.phtml Normal file
View File

@@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Elements\UnknownElement;
use Fisharebest\Webtrees\Elements\XrefAssociate;
use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\Gedcom;
use Fisharebest\Webtrees\GedcomRecord;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Media;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleInterface;
use Fisharebest\Webtrees\Module\RelationshipsChartModule;
use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Services\ModuleService;
use Fisharebest\Webtrees\Services\RelationshipService;
/**
* @var Fact $fact
* @var GedcomRecord $record
*/
$parent = $fact->record();
$tree = $parent->tree();
[, $tag] = explode(':', $fact->tag());
$label = $fact->label();
$value = $fact->value();
$type = $fact->attribute('TYPE');
$id = $fact->id();
$element = Registry::elementFactory()->make($fact->tag());
// New or deleted facts need different styling
$styles = [];
if ($fact->isPendingAddition()) {
$styles[] = 'wt-new';
}
if ($fact->isPendingDeletion()) {
$styles[] = 'wt-old';
}
// Event of close relative
if ($tag === 'EVEN' && $value === 'CLOSE_RELATIVE') {
$value = '';
$styles[] = 'wt-relation-fact collapse';
}
// Event of close associates
if ($id === 'asso') {
$styles[] = 'wt-associate-fact collapse';
}
if ($element instanceof UnknownElement && $tree->getPreference('HIDE_GEDCOM_ERRORS') === '0') {
$styles[] = 'd-none';
}
// historical facts
if ($id === 'histo') {
$styles[] = 'wt-historic-fact collapse';
}
// Family link facts — mark for hiding
if (in_array($tag, ['HUSB', 'WIFE', 'CHIL', 'FAMC', 'FAMS'], true)) {
$styles[] = 'bocken-family-fact';
}
// Use marriage type as the label. e.g. "Civil partnership"
if ($tag === 'MARR') {
$label = $fact->label();
$type = '';
}
?>
<tr class="<?= implode(' ', $styles) ?>">
<th scope="row">
<div class="wt-fact-label ut"><?= $label?></div>
<?php if ($id !== 'histo' && $id !== 'asso' && $fact->canEdit() && !in_array($tag, ['HUSB', 'WIFE', 'CHIL', 'FAMC', 'FAMS'], true)) : ?>
<?= view('fact-edit-links', ['fact' => $fact, 'url' => $record->url()]) ?>
<?php endif ?>
<?php if ($tree->getPreference('SHOW_FACT_ICONS') === '1') : ?>
<span class="wt-fact-icon wt-fact-icon-<?= e($tag) ?>" title="<?= strip_tags($label) ?>"></span>
<?php endif ?>
</th>
<td>
<?php if ($fact->target() instanceof Media) : ?>
<div class="d-flex flex-wrap">
<?php foreach ($fact->target()->mediaFiles() as $media_file) : ?>
<div class="me-1 mb-1">
<?= $media_file->displayImage(100, 100, 'contain', []) ?>
</div>
<?php endforeach ?>
</div>
<a href="<?= e($fact->target()->url()) ?>"><?= $fact->target()->fullName() ?></a>
<?php foreach ($fact->target()->facts(['NOTE']) as $note) : ?>
<?= view('fact-gedcom-fields', ['gedcom' => $note->gedcom(), 'parent' => $fact->target()->tag(), 'tree' => $fact->record()->tree()]) ?>
<?php endforeach ?>
<?php else : ?>
<div class="wt-fact-main-attributes">
<?php if ($parent !== $record) : ?>
<div class="wt-fact-record">
<?php if ($parent instanceof Family) : ?>
<?php foreach ($parent->spouses()->filter(static fn ($individual): bool => $individual !== $record) as $spouse) : ?>
<a href="<?= e($spouse->url()) ?>"><?= $spouse->fullName() ?></a>
<?php endforeach ?>
<a href="<?= e($parent->url()) ?>"><?= I18N::translate('View this family') ?></a>
<?php elseif ($parent instanceof Individual) : ?>
<a href="<?= e($parent->url()) ?>"><?= $parent->fullName() ?></a>
<?php endif ?>
</div>
<?php endif ?>
<div class="wt-fact-value">
<?= $element->value($value, $tree) ?>
<?php if ($element instanceof XrefAssociate && $fact->target() instanceof Individual) : ?>
<?php
$module = Registry::container()->get(ModuleService::class)->findByComponent(ModuleChartInterface::class, $tree, Auth::user())
->first(static fn (ModuleInterface $module): bool => $module instanceof RelationshipsChartModule)
?>
<?php if ($module instanceof RelationshipsChartModule && $record instanceof Individual) : ?>
<a href="<?= $module->chartUrl($fact->target(), ['xref2' => $record->xref()]) ?>" rel="nofollow">
<?= Registry::container()->get(RelationshipService::class)->getCloseRelationshipName($record, $fact->target()) ?>
</a>
<?php endif ?>
<?php endif ?>
</div>
<!-- Type of this fact/event (allow user-translations) -->
<?php if ($type !== '' && $tag !== 'EVEN' && $tag !== 'FACT') : ?>
<div class="wt-fact-type">
<?= Registry::elementFactory()->make($fact->tag() . ':TYPE')->labelValue(I18N::translate($type), $tree) ?>
</div>
<?php endif ?>
<?= view('fact-date', ['cal_link' => 'true', 'fact' => $fact, 'record' => $record, 'time' => true]) ?>
<?= view('fact-place', ['fact' => $fact, 'record' => $record]) ?>
</div>
<div class="wt-fact-other-attributes mt-2">
<?php preg_match_all('/\n2 (' . Gedcom::REGEX_TAG . ')( .*)?((\n[3-9].*)*)/', $fact->gedcom(), $matches, PREG_SET_ORDER) ?>
<?php foreach ($matches as $match) : ?>
<?php if (!in_array($match[1], ['DATE', 'AGE', 'HUSB', 'WIFE', 'PLAC', 'ASSO', '_ASSO', 'STAT', 'TEMP', 'TYPE', 'CONT', 'NOTE', 'OBJE', 'SOUR'], true)) : ?>
<?= view('fact-gedcom-fields', ['gedcom' => $match[0], 'parent' => $fact->tag() . ':' . $match[1], 'tree' => $tree]) ?>
<?php endif ?>
<?php endforeach; ?>
</div>
<?php if ($id !== 'asso') : ?>
<?= view('fact-associates', ['fact' => $fact]) ?>
<?php endif ?>
<?= view('fact-sources', ['fact' => $fact]) ?>
<?= view('fact-notes', ['fact' => $fact]) ?>
<?= view('fact-media', ['fact' => $fact]) ?>
<?php endif ?>
</td>
</tr>