Der Hauptzweck ist es Verknüpfungen zu Datensätzen in Stammbäumen zu vereinfachen. Dadurch müssen keine vollqualifizierten Links gespeichert werden, was die Portierbarkeit von GEDCOM-Daten einschränkt. Durch Verknüpfung von Notizen mit GEDCOM-Datensätzen (Personen, Familien, Quellen, usw.) wird das Geschichtenerzählen deutlich einfacher.
Support
Issues: Für Ideen oder beim Finden eines Bugs kann ein Issue erstellt werden.
Forum: Allgemeine webtrees-Unterstützung findet man im webtrees Forum.
1 - Einrichtung
Installation und Voraussetzungen
Was wird zum Installieren dieses Moduls benötigt und wie wird es korrekt eingerichtet.
1.1 - Voraussetzungen
Systemvoraussetzungen für das LinkEnhancer-Modul
Dieses Modul benötigt webtrees Version 2.2 oder 2.3
und hat dieselben Anforderungen wie webtrees#system-requirements.
Es wurde ursprünglich für Version 2.2.4 erstellt; seit LinkEnhancer 1.6.1 sollte es auch mit webtrees 2.3 funktionieren. Tests werden normalerweise mit eingebauten Themes und einigen anderen Custom-Modulen durchgeführt.
Tipp: Es wird empfohlen, das Cronjob-Modul ebenfalls zu installieren. Das erleichtert das Ausführen der CLI-Skripte zur Index-Wartung.
1.2 - Installation
Schritt-für-Schritt-Installationsanleitung
Über Custom Module Manager (empfohlen)
Installieren Sie Custom Module Manager für eine einfache und bequeme Installation von webtrees Custom-Modulen.
Öffnen Sie die Custom Module Manager-Ansicht in webtrees, wählen Sie “linkenhancer” aus und klicken Sie auf die “Install Module”-Schaltfläche.
Bei Aktivierung der ersten vier Grundfunktionen können weiter unten noch detailliertere Einstellungen vorgenommen werden. Auch gibt es zwei globale Einstellungen für Debugausgaben in der JavaScript-Konsole des Browsers und der Webserver-Fehlerlogdatei und man kann sich entscheiden, ob Links grundsätzlich in einem neuen Browsertab geöffnet werden sollen (je nach Grundfunktion differenzierte Einstellung ist auch möglich).
Nachfolgend die verfügbaren Optionen auf der Admin-Seite:
[1] Verbesserte Link-Unterstützung
Eigene externe Ziele
Hier zwei Beispiel-Einträge aus den vordefinierten Zielen zur Veranschaulichung des Prinzips, wie zusätzliche externe Ziele mit einem eigenen JavaScript-Objekt bereitgestellt werden können.
{
"fsft": {
name:'Family Search Tree - $ID',
url:'https://www.familysearch.org/tree/person/details/',
cname:'icon-fsft' },
"wp": {
name:'Wikipedia',
url: (id, title) => {
let parts = id.split('/');
if (parts.length <2) {
title = title +" - "+ options.i18n('syntax error') +"!";
return { url:'', title };
}
let subdomain = parts[0];
let article = parts.slice(1).join('/');
title =`${title} - `+decodeURIComponent(article) +` (${subdomain})`;
return { url:`https://${subdomain}.wikipedia.org/wiki/${article}`, title };
},
cname:'icon-wp',
help: [
{ n: options.i18n('wp-help1'), e:'de/Webtrees' },
{ n: options.i18n('wp-help2'), e:'en/Webtrees' },
{ n: options.i18n('wp-help3'), e:'other-subdomain/Webtrees' },
]
}
}
Erläuterung der Eigenschaften (die Syntax eines erweiterten Links: #@param-key=param-value):
key — der oben genannte Kurzname oder Query-Parameter-Schlüssel (param-key)
name — Titel oder Label, das als Link-Titel angezeigt wird; der Platzhalter $ID wird durch die gegebene ID (param-value) ersetzt
url — Service-URL, die aufgerufen wird; der Parameterwert / die gegebene Record-ID (param-value) wird ans Ende der URL angehängt. Es kann auch eine Funktion sein, die (id, title) akzeptiert und ein Objekt { url, title } zurückgibt.
cname — CSS-Klassenname(n), durch Leerzeichen getrennt
help — optionales Array von Objekten [{n:'', e:''},..] zur Veranschaulichung der Verwendung eines Nicht-Standard-Ziels durch Beispiele (in e), versehen mit Erläuterungstext (in n). Wird in der Markdown-Hilfe aufgelistet.
options.i18n ist eine Funktion, die von diesem Modul durchgereicht wird, um übersetzte Zeichenketten bereitzustellen.
Erforderliche CSS-Regeln am besten über das CSS-und-JS-Modul ergänzen. Eigentlich wird nur die Definition des Icons (Größe 30 x 30 Pixel reicht) als Hintergrundbild benötigt — referenziert als data: URLs. Zum Beispiel: .icon-whatever { background-image: url(...) }
[2] Markdown Verbesserungen
Wenn man als Entwickler den Markdown Editor (mde) auch auf Textareas auf den Bearbeitungsseiten anderer Custom-Module anwenden möchte, kann man dies mit dem MarkdownEditorActivationService tun (verfügbar seit v1.2.11 — Details siehe #101):
<?php
privatefunctiontoggleTinyMde(bool $enable, bool $force=false): void
{
$class="Schwendinger\\Webtrees\\Module\\LinkEnhancer\\Services\\MarkdownEditorActivationService";
if (Registry::container()->has($class)) {
/** @var MarkdownEditorActivationService $mde_service */$mde_service= Registry::container()->get($class);
$existingRule=$mde_service->getCustomRule(self::CUSTOM_TITLE);
if (
$force||$enable&&!$existingRule||!$enable&&$existingRule ) {
$mde_service->setCustomRule(
self::CUSTOM_TITLE, // module name as key
$enable? ["source-transcription-detail"] : [], // handler: route name
$enable? ["textarea[id$='_text']"] : [] // filter: querySelector
);
}
}
}
Dies funktioniert nur, wenn diese Funktion global aktiviert wurde und die Bearbeitungsseite mit einem Baum verbunden ist, der auch Markdown in seinen Notizen verwendet.
Im versteckten Textfeld unterhalb der Radiobuttons zum de-/aktivieren der Option “Markdown-Editor für Notizen-Texteingabefelder” lassen sich die registrierte Konsumenten durch Überfahren des Bereichs mit dem Mauscursor einsehen.
Bekannte Custom-Module, die diese Funktion nutzen:
Es können auf Wunsch in webtrees registrierte Routes importiert werden. Dies erleichtert die Abdeckung individueller Custom-Modul-Konfigurationen.
Außerdem können Daten im CSV-Format importiert und exportiert werden, um Änderungen bequemer zu gestalten. Man kann den Trenner und für den Import die Zeichencodierung definieren (beim Export ist es immer UTF-8). Man kann auf einen Blick sehen, wie viele Datenzeilen in der Tabelle gespeichert sind und wie viele von ihnen eine URL zugewiesen haben.
Im Falle von Diskrepanzen kann die Kontext-Hilfe durch Aktivieren der Option “Ausgabe von Debug-Informationen auf der JS-Konsole” leichter angepasst werden. Dies liefert Informationen zur aktiven Route und, falls zutreffend, zu Datentabelle-Einträgen, die für die Zuordnung zum Hilfe-Thema gefunden wurden.
Route-zu-Hilfe-Zuordnung
Die Zuordnung von Routes zu Hilfe-Artikeln im Handbuch wird in der Datenbank-Tabellele_route_help_map gespeichert. Dieses Modul kommt mit vordefinierten Zuordnungsvorschriften. Wenn etwas fehlt oder man ein Problem findet, zögern Sie nicht dies mitzuteilen.
Technische Hintergrundinformationen folgen. Die Tabelle hat die folgenden Header (die Header, die in einer CSV-Datei für den Import erforderlich sind, sind mit !! markiert):
id: automatischer Schlüssel — nicht relevant, das Datenfeld wird nur verwendet, um die Identifizierung des Datensatzes beim Bearbeiten zu erleichtern.
path!!: Routenpfad — entspricht dem Pfad der webtrees Pretty-URLs
handler!!: entspricht in der Regel dem PHP-Klassennamen des Codes, der die Anfrage bearbeitet
method!!: Web-Anfragemethode (GET, POST, HEAD). Nur GET-Routes sind allgemein relevant für die Zuordnung zu Handbuch-Abschnitten.
extras!!: PHP-Klassennamen des Zugriffslevels (Fisharebest\Webtrees\Http\Middleware\Auth*)
subcontext!!: CSS-Selektor-String oder JSON-Objekt-String, um eine Komponente/ein Subkontext-Thema auf der angegebenen Seite zu adressieren
category: Zeichenkettenwert zum besseren Gruppieren von Datenzeilen; nur der Wert ‘generic’ hat eine besondere Bedeutung
order!!: beliebiger numerischer Sortierschlüssel, übereinstimmende Datenzeilen werden aufsteigend sortiert (Standardwert ist 10)
url!!: Pfad der URL zum webtrees Handbuch (wird mit der angegebenen GenWiki-Basis/Domänen-URL verkettet). Auch vollqualifizierte URLs zu anderen Web-Ressourcen sind möglich und unterstützt.
updated_at: Zeitstempel der letzten Aktualisierung — hilft bei der Identifizierung möglicherweise veralteter Datenzeilen
Wie wir in app/Http/Routes/WebRoutes.php sehen können, wo die Standard-webtrees-Routes definiert sind, sind für eine Route die folgenden Informationen erforderlich:
path
handler
method
Optional kann extras bereitgestellt werden. Wenn keines der vier oben genannten Felder gesetzt ist, wird die Datenzeile vom Seeder gefiltert. Beim Import werden category und order automatisch gesetzt, falls nicht gesetzt.
Um eine Erstklassen-Übereinstimmung mit der aktuellen Route zu erzielen, müssen diese vier Felder übereinstimmen.
Normalerweise ist es nur notwendig, GET-Routes einem Artikel zuzuordnen. Es gibt auch einige Redirect-Routes und ajax-Helfer-Routes (die Daten für Formsteuerungen bereitstellen), die ignoriert werden können.
Sonderfälle sind die paths, die mit /module/{module}/{action} oder /tree/{tree}/data-fix/{data_fix} beginnen: sie werden von einer integrierten Proxy-Klasse verarbeitet, die das Custom-Modul über ein Route-Attribut adressiert:
Für die Zuordnung einer solchen Route zu einem bestimmten Custom-Modul-Hilfe-Artikel wird der handler auf den oben genannten Route-Attributwert gesetzt (z.B. _vesta_classic_look_and_feel_). Für eine generischere Regel kann path auch leer sein (dann kann eine Regel für beide Fälle verwendet werden).
Fallback-Regeln für Zugriffslevel werden durch category='generic' und den spezifischen Auth-Klassennamen in extras abgeglichen.
In aufsteigender Reihenfolge wird die erste Regel mit subcontext = '' für den Top-Menü-Link ausgewählt. Wenn nichts anderes zutrifft, zeigt der Link auf die Startseite des Handbuchs.
Wenn aktiviert, können subcontext-Themen auf einer Seite mit einem zusätzlichen Hilfe-Link versehen werden. Man kann einen CSS-Selektor-String oder einen JSON-Objekt-String mit den folgenden Eigenschaften setzen:
p: Position des Popovers, optional (Default ist top, andere mögliche Werte: left, right, bottom)
f: CSS-Selektor/Filter-String (nur notwendig, wenn man das Popover an einer anderen Positionieren möchte) ODER
e: JavaScript-Ausdruck-String
Dies ist nützlich, wenn es Komponenten auf der Seite gibt, die detailliertere Informationen benötigen, als der Hilfe-Link im Top-Menü bietet (z.B. für Custom-Modul-Funktionen auf der Personenseite). Dieser Hilfe-Link wird in einem Popover angezeigt (weil ein zusätzlicher Link nicht immer in Bootstrap-Tabs, Akkordeon-Headern oder Menü-Titeln funktioniert).
Setzen Sie category='generic' für Regeln, die auf mehr als eine Seite anwenden sollen.
Inhaltsverzeichnis (TOC)
Dieses Modul lädt ein mehrsprachiges Inhaltsverzeichnis für das deutsche webtrees Handbuch von einem Remote-Feed (JSON-Dateien: Struktur, Sprachübersetzungen und Versions-Manifest). Die Daten werden lokal zwischengespeichert und automatisch aktualisiert:
On-the-fly: Wenn ein Benutzer die TOC-Seite aufruft und der Cache abgelaufen ist (24 h), prüft der Service das Feed-Manifest (version.json) auf hash-Änderungen pro Datei und lädt nur die geänderten Dateien neu herunter.
Proaktiv (cron): Der refresh-wthb-toc Cron-Job (täglich um 03:00 per Default) lädt alle Dateien herunter, unabhängig vom Cache-Zustand.
Auto-Aktualisierung-Schalter: Die Modul-Admin-Seite bietet einen “Auto-Aktualisierung”-Schalter für die TOC-Daten. Wenn deaktiviert, werden nur lokal zwischengespeicherte Daten ausgeliefert (keine HTTP-Downloads). Der Admin-Force-Refresh-Button und die CLI mit --force funktionieren trotzdem unabhängig vom Schalter. Tipp: Daten vor dem Deaktivieren aktualisieren, um sicherzustellen, dass die neueste Version zwischengespeichert ist.
CLI:
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php # Scope aus Modul-Einstellung
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --all # alle verfügbaren Sprachen
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --lang=en,fr # bestimmte Sprachen
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --clear # nur Cache leeren
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --force # auch bei deaktivierter Auto-Aktualisierung
Während Auto-Aktualisierung ausgeschaltet ist, werden geplante Aktualisierungen übersprungen (der Cron-Lauf beendet sich mit Exit 0 ohne Arbeit). --force aktualisiert trotzdem (z.B. zur Vorbereitung der Reaktivierung), und --clear funktioniert unabhängig vom Schalter.
Externe Hilfe-Links
Das Modul enthält einige Standard-externe Hilfe-Links, die in JSON definiert sind — Array von Objekten mit den folgenden Mitgliedern:
title: Anzeigetext für den Link, kann auch HTML enthalten (String, Pflichtfeld), Sprache sollte en-US sein; für Standard-Link-Titel werden Übersetzungen bereitgestellt
self: falls gesetzt, ist Link-target_self (öffnet an Ort und Stelle), sonst _blank (beliebiger Typ, optional)
i18n: Titel mit benutzerdefinierter Übersetzung für gegebene Sprach-Tags überschreiben (Objekt, optional; Schlüssel sind Sprach-Tags, Werte sind übersetzte Titel)
Erklärung: Der Objektschlüsselname wird auf den Theme-Namen gesetzt, der abgeglichen werden soll, und der Zeichenkettenwert enthält eine oder mehrere CSS-Stilregeln. In diesem Beispiel haben wir eine Fallback-Regel mit dem Schlüssel *, die alle Themes abdeckt, es sei denn, es gibt einen spezifischen Schlüssel.
Theme colors ist ein Sonderfall, bei dem wir auch Paletten haben. Um diese Kombination zu adressieren, wird der Schlüssel auf theme name_palette name gesetzt. Der Schlüssel colors deckt alle Subthemes ab.
Aktivieren Sie einfach die Option “Ausgabe von Debug-Informationen auf der JS-Konsole”, um die korrekten Werte herauszufinden.
2 - Funktionen
Übersicht aller LinkEnhancer-Funktionen
2.1 - Erweiterte Links
Querverweise und LinkEnhancer-Links in GEDCOM-Datensätzen
Webtrees ersetzt XREFs wie @I2@ in Notizen durch einen Querverweis und dessen Anzeige-Name — aber das ist nicht sehr flexibel. Man kann den Anzeigenamen nicht festlegen, was bedeutet, dass bei einem Fehler (durch XREF-Umnummerierung oder Tippfehler) die Auflösung des Querverweises nicht leicht oder gar nicht möglich ist.
Tipp: Das Custom-Modul webtrees-mitalteli-show-xref kann helfen, die XREF- und UID-Referenz von Personendatensätzen zu finden. Einige UIDs (38 Zeichen Länge) werden von webtrees in der Standardansicht automatisch verkürzt — dieses Modul zeigt die unveränderten UIDs.
Funktionsweise
Die erweiterte Links-Funktion ist per JavaScript implementiert und sucht in der gerenderten webtrees-Ausgabe nach <a>-Tags, deren href-Attribut mit #@ beginnt. Dadurch können Links in Markdown und HTML-Markup verarbeitet werden. Da die Verarbeitung im Browser auf Client-Seite erfolgt, wird die Existenz der Querverweis-Datensätze nicht im Voraus geprüft. Fehler treten erst auf, wenn der Link angeklickt wird (z.B. wenn der Zugriff auf die Ressource eingeschränkt ist).
Als Bonus können mit einer Link-Definition verschiedene Ziele adressiert werden, wobei der Querverweis immer der erste Link ist (falls gesetzt) und die anderen nur durch angehängte klickbare Icons dargestellt werden.
Vordefinierte externe Ziele
Die Parameter-Schlüssel sind in Klammern aufgeführt:
Jeder Parameter-Schlüssel kann mehrfach angegeben werden — aber man sollte es der Klarheit halber nicht übertreiben.
Zusätzliche externe Ziele können auf der Admin-Seite konfiguriert werden.
Syntax
Markdown: Ein erweiterter Link sieht so aus: [Link-Anzeigename](#@param1¶mN), sodass ein oder mehrere Ziele auf einmal adressiert werden können. Für Querverweise in webtrees wird eine Datensatz-Referenz erwartet (REF kann die XREF oder eine UID sein):
wt=@REF@ oder wt=@REF@andererbaum — Standard-Link, Zugriff auf die Datensatzseite über Redirect-URL
wt=n@REF@ — Legacy-Link mit gegebenem Record-Typ (hier: zu einer Notiz mit REF im aktuellen Baum)
Verfügbare Record-Typen: i=Person, f=Familie, s=Quelle, r=Depot, n=Notiz, l=geteilter Ort
wt=i@REF@andererbaum+dia — Link zum Personentyp mit REF aus Baum “andererbaum” und zusätzlich Link zum Interaktiven Sanduhrdiagramm dieser Person
HTML: Dasselbe gilt für HTML-Links: <a href="#@wt=i@I1@">Link-Anzeigename</a>
Dies ist auch in Kombination mit der Namensschild-Funktion des Vesta Classic Look & Feel Moduls im HTML-Snippet-Feld verwendbar: <a href="#@fsft=<ref/>"></a> zum Verlinken eines Datensatzes im Family Search Family Tree. Es ist auch möglich, solche Links in News-Artikeln von Tyweds news-menu Modul oder in FAQ-Artikeln einzubetten.
Die Syntax der externen Ziele wird von der Markdown-Hilfe-Funktion dieses Moduls aufgelistet. In den meisten Fällen muss nur ein einzelnes Schlüssel-Wert-Parameterpaar angegeben werden, bestehend aus dem Kurznamen des gewünschten Ziels und der ID des dort befindlichen Datensatzes.
Beispiel
Im oberen Teil des Bildes ist der Bearbeitungsmodus eines Notizdatensatzes zu sehen, unten die Darstellung im Anzeigemodus:
2.1.1 - Querverweis-Übersicht
Querverweis-Übersicht mit Link-Index für große Bäume
Es gibt drei Arten von Übersichten:
Übersicht über alle Daten für den Admin (Admin-Übersicht),
auf einen Stammbaum begrenzte Übersicht (Benutzer-Übersicht) und
auf einen Datensatz bezogene Übersicht in der Detailansicht (Datensatz-Übersicht, als Tab oder Modal).
Die Admin-Übersicht (Verwaltung → LinkEnhancer → Querverweis-Übersicht) ist eine serverseitig paginierte DataTable. Sie listet alle Datensätze auf, die klassische @XREF@-Querverweise oder LinkEnhancer-Links enthalten, mit einer Link-Auflistung pro-Datensatz.
Optionale Filter:
referenzierte XREF (nur verfügbar, während ein frischer Link-Index aktiv ist — im Live-Scan-Modus ist das Feld ausgeblendet, da es dort nur ein grober Vorfilter wäre — keine klassischen Referenzen würden gefunden),
Datensatz-Typ,
Stammbaum und
die Anzahl der pro Link-Klasse angezeigten Token (alle / 5 / 10 / 20, Default 5).
Die XREF-Zelle zeigt den Stammbaumnamen als zweite Zeile in dezenter Schrift. Der Datensatz-Typ ist eine eigene sortierbare Spalte. Jedes Inventar-Token wird als kurzer Anzeigekontext (Snippet) mit dem Link-Token selbst in Fett angezeigt.
Optional kann man eine Benutzer-Übersicht pro Baum und eine Datensatz-Übersicht über die ein- und ausgehenden Referenzen pro GEDCOM-Datensatz aktivieren. Die Datensatz-Übersicht zeigt zudem die XREF und ggf. UIDs des Datensatzes an, damit diese Referenzen leichter in Notzen übernommen werden können.
Unterstützte Engines
Live-Übersicht
Link-Index (CLI)
MariaDB / MySQL
ja (REGEXP-Gate)
ja
PostgreSQL
ja (REGEXP-Gate)
ja
SQLite / SQL Server
Eingeschränkter Modus — automatischer Fallback auf ein grobes LIKE-Gate + Volltabellen-Scan pro Anfrage (Banner auf der Seite); die exakte Klassifizierung erfolgt weiterhin in PHP
nicht verfügbar (braucht REGEXP + MD5) — die CLI beendet sich mit einem Fehler
Funktionsweise
Bei großen Bäumen kann der Live-Regex-Scan über die GEDCOM-Tabellen langsam sein, daher kann die Seite stattdessen einen Link-Index aus drei Modultabellen lesen (der Abschnitt Entwicklung / Projekt-Struktur enthält mehr Details zum Datenbank-Schema):
le_record_scan — eine Zeile pro gescanntem Datensatz mit einem MD5-Fingerprint des Datensatztextes
le_link_index — eine Zeile pro (Link, Ziel), das im Datensatz gefunden wurde (der zum Build-Zeitpunkt erfasste Anzeigekontext) — das Fundament für Anzeige der eingehenden Querverweise in der Datensatz-Übersicht
le_index_meta — einzelne Zeile — der Zustand des letzten vollständigen Index-Laufs
flowchart TD
CLI["🛠 cli/build-link-index.php<br/>Cron: */30 * * * *"]
subgraph Scan["🔍 Inkrementeller Scan"]
FP["MD5-Fingerabdruck<br/>pro Record"]
DIFF{"🔀 Geändert?<br/>(neu / entfernt)"}
end
subgraph Tables["💾 Tabellen"]
RS[("le_record_scan<br/>file, xref, hash")]
LI[("le_link_index<br/>token, target, snippet")]
META[("le_index_meta<br/>last_run, rows")]
end
PAGE["📄 XREF-Übersichtsseite"]
CLI --> Scan
FP --> DIFF
DIFF -->|✅ ja| SCAN["PHP-Scan<br/>(XrefsService)"]
DIFF -->|⏭ nein| SKIP["überspringen"]
SCAN --> RS
SCAN --> LI
CLI --> META
META -->|"🟢 fresh (< 2 h)"| PAGE
META -->|"🟠 stale"| LIVE["Live-Regex-Scan"]
LIVE --> PAGE
classDef scanStyle fill:#e3f2fd,stroke:#1565C0,stroke-width:2px
classDef tablesStyle fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px
classDef decideStyle fill:#fff3e0,stroke:#E65100,stroke-width:2px
classDef pageStyle fill:#fce4ec,stroke:#C2185B,stroke-width:2px
class FP scanStyle
class RS,LI,META tablesStyle
class DIFF decideStyle
class PAGE pageStyle
Initialer Build (erster Lauf oder --rebuild): Beide Indextabellen werden zurückgesetzt, alle Scan-Zeilen werden serverseitig in einer Bulk-Statement pro GEDCOM-Tabelle erstellt (schnell, null Zeilentransfer), und nur die Datensätze, die den Link-Vorfilter passieren, werden in PHP gescannt. Der initiale Build ist eine Einmal-Kosten — verwenden Sie einen höheren --limit dafür. Wenn er durch --limit unterbrochen wird, konvergiert der nächste Lauf (der Build ist idempotent).
Inkrementelle Läufe (der normale Cron-Fall): Nur Datensätze, deren MD5-Fingerprint sich geändert hat — oder die hinzugefügt/entfernt wurden — werden neu gescannt (cursor chunked), sodass der Job auch auf großen Sites günstig bleibt.
Cron-Beispiel (alle 30 Minuten, max. 2000 Datensätze pro Lauf):
Nur ein vollständiger Lauf (ohne --tree), der nicht durch --limit gekürzt wird, markiert den Index als frisch (le_index_meta.last_run). Ein durch --limit gekürzter Lauf setzt dort fort, wo er aufgehört hat; ein --tree-Lauf aktualisiert nur diesen Baum.
Index leeren (Flush)
--flush leert die Indextabellen und beendet sich (kein Rebuild) — für Tests (die Seite auf Live-Scan zwingen) und Wartung (sauberer Neustart bei kaputtem Index):
php modules_v4/linkenhancer/cli/build-link-index.php --flush # alle Bäume
php modules_v4/linkenhancer/cli/build-link-index.php --flush --tree=5 # nur Baum 5
Das Frische-Flag wird immer zurückgesetzt, sodass die Seite zuverlässig auf den Live-Scan zurückfällt. Der nächste vollständige Lauf ist wieder ein initialer Build (Voll-Scan-Kosten). --flush kombiniert mit --rebuild ist redundant — --rebuild setzt bereits zurück und baut neu.
Was zählt als Link
Die Übersicht und der Index verwenden einen gemeinsamen Vorfilter (einzelne Quelle: XrefsService::linkPrefilterPatterns()):
NOTE (Level >= 1) mit @XREF@oder](#@-Link, mit Inhalt auf derselben Zeile. Ein NOTE-Wert, der exactly eine Referenz ist (1 NOTE @N5@), ist ein GEDCOM-gemeinsamer Notiz-Pointer — eine strukturelle Referenz, kein Text-Link, und wird nicht aufgelistet.
CONC/CONT/TEXT/_TODO mit @XREF@ (nackt ist ein Link) oder ](#@-Link
ein LE-Link kann einen optionalen id=@XREF@-Parameter tragen (maximal einer, die Position unter den Parametern ist egal) — wie die wt=-Parameter wird er als Link-Ziel indexiert
der eigene Level-0-Text einer gemeinsamen Notiz (0 @N1@ NOTE …)
die Übereinstimmung ist zeilenbasiert (keine Cross-Line-Matches), auf denselben Zeilen, die der Collector liest
Die Seite zeigt an, welche Quelle sie verwendet:
Index aktiv (grüne Notiz): ein vollständiger, kompletter Index-Lauf ist vor weniger als 2 Stunden beendet (Default; XrefsService::INDEX_FRESH_SECONDS)
Live-Scan (blaue Notiz): kein frischer Index — die Seite fällt auf den Live-Scan zurück
Eingeschränkter Modus (gelbe Notiz, SQLite/SQL Server): siehe Enginetabelle oben
Verwenden Sie p1-measure.php
(read-only) auf Ihrer Instanz, um zu prüfen, wie kostspielig der Live-Scan für Ihre Daten ist und den Cron-Zeitplan zu kalibrieren.
2.1.2 - xref_IDs neu nummerieren (mit Verweisen)
XREFs umnummerieren mit automatischer Link-Reparatur
Die webtrees Standardfunktion für die Umnummerierung (Verwaltung → Stammbäume verwalten → [Stammbaum] → xref_IDs neu nummerieren) löst Stammbaumübergreifende XREF-Kollisionen auf, aktualisiert aber keine LinkEnhancer- oder klassischen Links. Dieses Modul bietet eine erweiterte Umnummerierungsfunktion, die alle betroffenen Links in einem Schritt repariert.
Zugriff: Verwaltung → LinkEnhancer → xref_IDs neu nummerieren (mit Verweisen)
Ablauf:
Den Stammbaum mit XREF-Kollisionen auswählen (doppelte XREFs mit anderen Bäumen).
Die Seite zeigt jede kollidierende XREF, ihren Record-Typ und die Anzahl der eingehenden LinkEnhancer-Referenzen aus dem Link-Index.
Beim Absenden wird jede kollidierende XREF:
in den GEDCOM-Core-Tabellen umbenannt (gleich wie die Standard-Umnummerierung)
alle wt=@XREF@ / id=@XREF@-Links in Notizen und Blöcken aktualisiert
der Link-Index (le_link_index) und UID-Index (le_uid_index) werden inline oder an Cron delegiert repariert
Jeder umnummerierte Datensatz wird im Edit-Log protokolliert (Admin → Änderungslog), z.B. Renumbered @I456@ → @I457@ (INDI, 3 core rows, 2 links).
Wenn die PHP-Ausführungszeitgrenze erreicht wird, stoppt der Lauf mit teilweisem Fortschritt — die verbleibenden Kollisionen werden im nächsten Lauf umnummeriert.
Hinweis: Die Standard-webtrees-Umnummerierung (ohne Links) bleibt verfügbar und ist nicht betroffen. Verwenden Sie die Umnummerierung dieses Moduls, wenn Ihre Notizen oder Blöcke LinkEnhancer-Links enthalten.
Siehe auch: fisharebest/webtrees#5156
2.1.3 - Datenkorrekturen
Datenkorrektur-Handler für Link-Integritätsprobleme
Das Modul bietet eine Reihe von Datenkorrektur-Handlern, die über die webtrees Datenkorrektur-Schnittstelle erreichbar sind (Steuerungspanel → Datenkorrekturen → LinkEnhancer). Jeder Handler adressiert ein spezifisches Link-Integritätsproblem:
Korrektur
Was sie tut
XREF ⇄ UID Referenz-Tausch
Konvertiert LinkEnhancer-Link-Ziele zwischen XREF- und UID-Form. Richtung ist wählbar (XREF→UID oder UID→XREF). Erfordert, dass der UID-Index aufgebaut ist.
wt= Typ-Buchstabe korrigieren
Setzt, repariert oder entfernt den Legacy-Typ-Buchstaben in wt=<type>@REF@-Links. Modi: setzen (fehlenden Typ hinzufügen), reparieren (falschen Typ korrigieren), entfernen (Typ-Buchstaben streichen).
Verwaiste Link-Ziele melden
Nur Bericht: Listet LinkEnhancer-Links auf, deren Ziel-Datensatz nicht mehr existiert. Es werden keine Änderungen vorgenommen.
Mehrdeutige UID-Ziele melden
Nur Bericht: Listet Links auf, deren UID-Ziel auf mehr als einen Datensatz auflöst. Es werden keine Änderungen vorgenommen.
Doppelte UIDs im Index melden
Nur Bericht: Listet UIDs auf, die im UID-Index mehreren Datensätzen zugewiesen sind. Es werden keine Änderungen vorgenommen.
Jede Korrektur folgt dem webtrees Datenkorrektur-Workflow: Korrekturtyp wählen → Vorschau → anwenden. Die Datenkorrektur-Seite zeigt auch den aktuellen Status der Link- und UID-Indizes und ob Index-Aktualisierungen an den Cron-Job delegiert werden.
2.2 - Markdown
Markdown-Rendering in Notizdatensätzen
Markdown ist ein einfaches Formatierungssystem, das auf Websites wie Wikipedia oder GitHub verwendet wird. Es nutzt unauffällige Satzzeichen, um Überschriften, fett/kursiv Text, Listen, Tabellen usw. zu erstellen.
Webtrees verwendet eine CommonMark-Implementierung und unterstützt eine Untermenge des Markup. Es ist optional in Notizdatensätzen auf Baum-Ebene verfügbar.
Hinweis: Für die Markdown-Bild-Unterstützung und die zusätzlichen Markdown-Erweiterungen muss die MarkdownFactory-Klasse durch dieses Modul erweitert werden. Diese Ersatzklasse behebt auch fisharebest/webtrees#4841, sodass ein vorformatierter Markdown-Block nicht mehr als eine einzige Zeile gerendert wird.
2.2.1 - Markdown-Bild-Unterstützung
Bilder aus GEDCOM-Mediendatensätzen oder öffentlichen Ordner in Notizen einbetten
Diese Funktion erlaubt es, Bilder aus GEDCOM-Mediendatensätzen (die hinter der Media-Firewall liegen) oder aus dem öffentlichen Verzeichnis in Notizen einzubetten. Wenn Restriktionsregeln auf den Datensatz zutreffen, wird statt des Bildes eine Meldung angezeigt.
Syntax:



Technisch werden die Bilder in einen div-Container zusammen mit einem Bild-Untertitel gepackt — der auch ein Link zum Mediendatensatz ist. Die Darstellung kann wie gewünscht mit den Standard-CSS-Klassen oder zusätzlichen per-Bild-CSS-Klassen angepasst werden (z.B. float-start oder float-end aus webtrees vendor.css).
2.2.2 - Markdown-Editor
Visueller Markdown-Editor (TinyMDE) für Notizfelder
Neben der Syntax-Hervorhebung wird eine Icon-Leiste für gängige Formatierungsbefehle, ein Hilfe-Popup und Zeilennummerierung mitgeliefert.
Hinweis: Die On-Screen-Tastatur funktioniert leider nicht mehr wie zuvor mit dem vorherigen Texteingabefeld. Die gewählten Zeichen landen als Zwischenschritt im kleinen Textfeld unter dem Markdown-Editor und müssen manuell an die gewünschte Position kopiert werden.
Warnung: Bitte bedenken Sie bei der Verwendung dieser Erweiterungen, dass Sie vom kleinsten gemeinsamen Markdown-Dialekt abweichen. Wenn die Bibliothek, die Markdown in HTML rendert, aus irgendeinem Grund geändert werden muss, ist es möglich, dass nicht alle hier verwendeten erweiterten Markup unterstützt werden (siehe auch #32). Da das Markup nicht aufdringlich ist, sollte der Impact nicht gravierend sein — natürlich ist es ein kleiner Nachteil.
Das Paket League\CommonMarkdown, das zur Verarbeitung von Markdown-Texten verwendet wird, bietet mehrere nützliche Erweiterungen, die optional aktiviert werden können:
Man kann alle oder einige ausgewählte dieser Erweiterungen aktivieren. Soweit möglich sind sie natürlich mit dem Markdown-Editor integriert und man findet zusätzliche Informationen in der Markdown-Hilfe.
Fußnoten
Fußnoten können in der Nähe ihrer Verweise platziert werden (es kann ein oder mehrere Verweise auf eine Fußnote geben) oder überall dort, wo man möchte. Für den Überblick ist es ratsam, sie unten anzubringen. Beim Rendering befindet sich der Fußnoten-Container am Ende des Inhalts.
Inhaltsverzeichnis
Wenn der Text mit Überschriften strukturiert ist, ist es eine gute Wahl, die Inhaltsverzeichnis-Erweiterung zu aktivieren. Das Inhaltsverzeichnis wird automatisch generiert und in den Inhalt über der allerersten Überschrift oder anstelle eines Platzhalters eingefügt.
2.2.4 - Tabellen-Zellenhöhe steuern
Für längere Markdown-Notiztexte ist es manchmal mühsam, scrollen zu müssen, wenn man sich für die Fakten darunter interessiert. Diese Funktion sollte hier weiterhelfen: Durch Aktivieren des Häkchens in der oberen rechten Ecke der Titelspalte (erste Tabellenspalte) wird die maximale Höhe der Inhaltszelle mit der langen Notiz auf ca. 80% der Viewport-Höhe gesetzt. Damit wird der Inhalt selbst scrollbar.
Wo anwendbar, kann die Funktion standardmäßig aktiviert (oder einfach verfügbar gemacht) und auch unabhängig deaktiviert werden.
In Kombination mit der Inhaltsverzeichnis-Erweiterung (Position = Dropdown) kann man den Text leicht durchnavigieren — vorausgesetzt, der Text ist mit Überschriften strukturiert.
Man kann entscheiden, ob das Häkchen immer sichtbar ist oder erst beim Überfahren der Fakt-Titelspalte mit dem Mauscursor. Wenn die Textlänge der Inhaltszelle die definierte Maximalhöhe nicht überschreitet, kann das Häkchen nicht aktiviert werden.
2.3 - Deutsches webtrees Handbuch (WTHB)
Kontextsensitive Hilfe-Links zum deutschen webtrees Handbuch
Ein kontextsensitiver Hilfe-Link zum deutschen webtrees Handbuch kann dem kleinen Navigationsmenü hinzugefügt werden. Kontext-Links sind auch für Komponenten auf Seiten von Custom-Modulen verfügbar (z.B. auf der Personenseite, der Startseite oder bei Elementen im Hauptmenü) — diese sind an den i-Symbolen in einem Kreis erkennbar.
Zur Sicherstellung, dass diese Funktion auch im Admin-Bereich verfügbar ist, muss eine modifizierte Version von views/layouts/administration.phtml registriert werden. Dieses Modul versucht, das Custom-Modul vesta_common zu verwenden (falls installiert und aktiviert) oder seine eigene Version der View-Layout-Datei. Alternativ kann auch Patch P002 auf den Core-Code angewendet werden.
Wenn die webtrees-Seite in einer anderen Sprache als Deutsch angezeigt wird, ist es möglich, die webtrees-Handbuch-URL über einen Übersetzungsdienst zu öffnen (Google Translate). Dies kann als Site-Einstellung aktiviert/deaktiviert oder dem Besucher überlassen werden.
Hinweis: Aufgrund von AI-Bot-Einschränkungen auf dem GenWiki ist diese Funktion derzeit nutzlos.
Der Einstellungsdialog kann über das Hilfe-Untermenü im oberen Menü aufgerufen werden. Wenn diese Option aktiviert ist, findet man auch einen Link zu einem Dialogfeld, das eine Volltextsuche im deutschen webtrees Handbuch (mit verschiedenen Suchmaschinen) und das Inhaltsverzeichnis anzeigt. Wie das TOC selbst erzeugt wird (GenWiki-Rundlauf, Generierung, Weblate) steht auf der Seite WTHB-TOC-Language-Pipeline.
Untermenü
Zusätzlich enthält das Untermenü eine Übersicht der im Handbuch enthaltenen Hilfe-Themen und anpassbare externe Hilfe-Links (z.B. webtrees FAQ und Forum, GitHub-Repo, etc.). Der obere Menüeintrag kann in zwei Links aufgeteilt werden — einer zum direkten Öffnen des Kontext-Hilfe-Artikels und der andere zum Öffnen des Untermenüs.
2.4 - Home-Link
Seitentitel als konfigurierbarer Link mit themenbewusstem CSS
Der Seitentitel kann ein Link zur Baum-Startseite oder zur Benutzer-Mein-Seite sein. Der Webmaster kann auch eine benutzerdefinierte URL setzen.
Dynamische CSS-Stile können angewendet werden, um dem aktuellen Theme zu entsprechen und sicherzustellen, dass die Anzeige gut kontrastiert. Natürlich ist es nicht auf den Home-Link-Fall beschränkt - Details siehe Admin-Einstellungen.
2.5 - UID-Suche
Navigation zu einem Datensatz über seine persistente UID
Eine UID (_UID in GEDCOM 5.5.1, UID in GEDCOM 7.0) ist eine persistente Kennung, die einem Datensatz zugewiesen wird. Die UID übersteht XREF-Umnummerierungen und Baum-Migrationen und ist damit ein stabiler Referenzpunkt.
Diese Funktion ermöglicht die direkte Navigation zu einem Datensatz über seine UID. Das URL-Muster ist:
/tree/{baum}/goto-uid/{uid} — auf einen Baum beschränkt
/goto-uid/{uid} — global (durchsucht alle Bäume)
Verhalten:
0 Treffer → 404
1 sichtbarer Treffer → Weiterleitung zum Datensatz
>1 sichtbarer Treffer → Auswahl-Liste (UIDs mit unterschiedlicher Groß/Klein-Schreibung oder dieselbe UID in mehreren Datensätzen)
Der UID-Vergleich ist case-insensitive (GEDCOM-Spezifikationen definieren UID nicht als case-sensitive). Der gespeicherte Wert ist wörtlich; die Übereinstimmung wird case-insensitiv geprüft, sodass UIDs mit unterschiedlicher Groß/Klein-Schreibung eine Auswahl-Liste zurückliefern statt einer möglicherweise falschen einzelnen Weiterleitung.
UIDs können über die Schnellsuche gesucht werden. Für kürzere UIDs, die als XREF durchgehen könnten, kann das Präfix uid: vorangestellt werden.
Standard-Navigation (goto-id)
goto-id ist das Standard-Navigationsziel für jeden LinkEnhancer-Link — sowohl untypisiert (wt=@REF@) als auch typisiert (wt=<type>@REF@) — und löst eine Referenz auf, die entweder ein XREF oder eine UID ist. Das URL-Muster ist:
/tree/{baum}/goto-id/{id} — auf einen Baum beschränkt (fällt auf globale Suche zurück, wenn im Baum nichts gefunden wird, mit Flash-Benachrichtigung)
/goto-id/{id} — global (durchsucht alle Bäume)
Verhalten (gemeinsamer IdResolver): Die ID wird längenbasiert und bidirektional versucht — eine lange ID (≥ 18 Zeichen) zuerst als UID, eine kurze zuerst als XREF — und fällt auf die andere zurück, wenn die erste nichts ergibt, sodass eine Auflösung nie verloren geht. 0 Treffer → 404, 1 sichtbarer Treffer → Weiterleitung, >1 → Auswahl-Liste.
sequenceDiagram
autonumber
participant U as 👤 Nutzer (Browser)
participant JS as LE JavaScript
participant WT as webtrees-Server
participant IR as IdResolver
participant DB as Datenbank
Note over U,JS: Klick auf einen LE-Link
U->>JS: Klick auf #@wt=@I234@
JS->>JS: detect href "#@…"
JS->>WT: GET /tree/5/goto-id/I234
WT->>IR: resolve("I234", tree=5)
alt ID short (< 18 chars) → try as XREF first
IR->>DB: SELECT … WHERE xref = 'I234'
DB-->>IR: Treffer?
else ID long (>= 18 chars) → try as UID first
IR->>DB: SELECT … FROM le_uid_index WHERE uid = '…'
DB-->>IR: Treffer?
end
alt ✅ 1 Treffer
IR-->>WT: Record
WT-->>U: Weiterleitung zur Record-Seite
else 0 Treffer → Fallback
IR->>IR: andere Richtung versuchen (XREF ⇄ UID)
else >1 matches
WT-->>U: Auswahl-Liste
end
Note over IR,DB: Bidirektional — XREF ⇄ UID<br/>eine Auflösung geht nie verloren
Ein typisierter wt=<type>@REF@-Link ist eine Legacy-Form: der Typ-Buchstabe wird nicht mehr für das Routing verwendet (alle Links gehen über goto-id) und ist nur dokumentarisch — er steuert weiterhin den Diagramm-Link für Personen und den Typ-Mismatch-Hinweis in der Querverweis-Übersicht. Neue Links sollten die untypisierte wt=@REF@-Form verwenden.
XREF-Route (goto-xref)
Es ist auch die Route /tree/{baum}/goto-xref/{xref} verfügbar, die es erlaubt, unabhängig vom Record-Typ zum entsprechenden Datensatz im Baum zu navigieren.
Querverweis-Übersicht: UID-Ziele in anderen Bäumen
In der Admin-Querverweis-Übersicht wird ein Link, dessen UID-Ziel nicht im eigenen Baum des Datensatzes gefunden wird, normalerweise als “Ziel nicht gefunden” angezeigt. Wenn die UID-Funktion aktiviert ist, wird ein solches Ziel zusätzlich global (über die anderen Bäume hinweg) aufgelöst; wenn Treffer existieren, wird es als nicht eindeutig (?) mit der Trefferanzahl angezeigt: bis zu 3 Treffer werden als direkte Cross-Tree-Links aufgelistet, mehr als 3 kollabieren zu einem einzelnen goto-id-Link (Titel = die UID). Dieses Verhalten ist hart an der UID-Funktions-Einstellung gated und gilt nie für Ziele, die einen expliziten Baum benennen.
Index
Der UID-Index wird in der Tabelle le_uid_index gespeichert (eine Zeile pro UID-Tag-Vorkommen, unterschieden durch tag_path). Er wird ausschließlich durch build-uid-index.php
gepflegt (kein Live-Scan bei Anfragen).
Initialer Build (erster Lauf oder --rebuild): Vollständiger Scan aller GEDCOM-Tabellen, Extraktion aller UID-Tags via UidTagCollector.
Inkrementelle Läufe: Nur Datensätze, deren MD5-Fingerprint sich geändert hat (oder hinzugefügt/entfernt wurden), werden neu gescannt.
Cron-Beispiel (alle 10 Minuten, max. 5000 Datensätze pro Lauf):
Die Funktion kann auf der Modul-Admin-Seite ein-/ausgeschaltet werden (“Aktivieren” → [5] UID-Suche). Der aktuelle Index-Status (Zeilenanzahl, letzter Build-Zeitpunkt) wird dort angezeigt.
Während die Funktion ausgeschaltet ist, werden Index-Builds automatisch übersprungen (der Cron-Lauf beendet sich mit Exit 0 ohne Arbeit). --force baut den Index trotzdem (z.B. zur Vorbereitung der Reaktivierung), und --flush funktioniert unabhängig vom Schalter.
Hinweis: UID-Tags auf jeder Verschachtelungsebene werden indexiert (z.B. 0 @I1@ INDI, 1 _UID, 1 FAMF, 2 _UID). Der Tag-Pfad wird pro Zeile gespeichert, sodass Duplikate in der Auswahl-Liste unterscheidbar sind.
2.6 - Patches
Optionale webtrees Core-Patches
Die Patches können zusätzlich angewendet werden — das Modul funktioniert auch ohne sie!
Der util-Unterordner enthält kleine Patches für den webtrees-Core. Dies sind Diff-Dateien, die per Shell aus dem installierten Modulordner heraus leicht angewendet oder entfernt werden können (notwendig für die Auto-Erkennung der webtrees-Quellen):
Usage: util/wt-patch.sh [-R][FILTER] -R Patch rückgängig machen (patch -R) FILTER '*' für alle oder spezifisch (z.B. '01')
Vergessen Sie nicht, die Patches nach einem webtrees-Update neu anzuwenden.
Es handelt sich um kleine Bugfixes oder Funktionserweiterungen — meist in einer einzelnen Datei — die dazu dienen, die Lücke bis zu einer offiziellen Behebung/Implementierung im webtrees-Core zu überbrücken.
#
Beschreibung
Gilt für Version
P001
Backlink für Level-1-gemeinsame Notizen #5181 — nicht mehr notwendig, es sei denn, man möchte einen zusätzlichen Edit-Link. app/Fact.php
2.2.1 - 2.2.6
P002
headContent/bodyContent für dieses Modul im Admin-Bereich aktivieren, um den Kontext-Hilfe-Link anzuzeigen #5214. resources/views/layouts/administration.phtml
2.2.1 - 2.2.6
P003
Datensatz hat mehrere uid-Felder #4828. app/Services/GedcomEditService.php
2.2.1
3 - Entwicklung
Entwicklungs-Dokumentation
3.1 - CLI-Skripte
Command-Line-Wartungsskripte
Das Modul bringt ausschließlich CLI-Skripte mit. Sie sind geschützt: bei Aufruf über HTTP (das Verzeichnis modules_v4 liegt innerhalb des Web-Roots) antworten sie mit 403 anstatt auszuführen.
Wenn Sie zu diesem Modul beitragen möchten, großartig! Sie können beitragen, indem Sie:
Code beisteuern — schauen Sie sich die Issues für Dinge an, die Aufmerksamkeit brauchen. Wenn Sie Änderungen vornehmen möchten, die nicht in einem Issue aufgeführt sind, erstellen Sie bitte eines, damit Sie Ihren Pull Request verlinken können.
Testen — derzeit ist alles manuell; bitte erstellen Sie ein Issue für jeden Bug, den Sie finden.
Übersetzung
Sie können zu diesem Projekt auf Codeberg Weblate beitragen. Aktualisierte Übersetzungen werden in der nächsten Version dieses Moduls enthalten sein.
Das Modul besitzt fünf Tabellen (Präfix le_), die inkrementell durch src/Schema/Migration#.php erstellt werden. Jede Migration ist additiv und idempotent, sodass das Schema bei jedem Modul-Load konvergiert.
le_route_help_map
Ordnet eine webtrees-Route (path / handler / method) einem Hilfe-Eintrag zu — der Speicher für die kontextsensitive Handbuch-Hilfe. Wird von SeedHelpTable befüllt.
Spalte
Typ
Hinweise
id
int, PK (auto)
path
varchar(255)
Routen-Pfad
handler
varchar(150)
vollqualifizierte Handler-Klasse
handler_key
varchar(150), index
versionsneutrale Key (2.2.6 + 2.3 kompatibel)
method
varchar(20)
extras
varchar(60)
subcontext
varchar(250)
category
varchar(60)
standard / custom module / generic …
order
int, Default 10
url
varchar(250)
updated_at
timestamp, nullable
Index auf (path, handler) und auf handler_key.
le_record_scan
Eine Zeile pro gescanntem GEDCOM-Datensatz, mit einem MD5-Fingerprint des Datensatztextes — lässt dem CLI-Index eine inkrementelle Diff-Prüfung zu.
Spalte
Typ
Hinweise
file
int
Baum-ID (Teil des PK)
xref
varchar(20)
(Teil des PK)
rectype
varchar(15)
GEDCOM-Tag, z.B. INDI, OBJE (Teil des PK)
hash
varchar(32)
MD5 des Datensatztextes
scanned_at
timestamp, nullable
Primärschlüssel (file, xref, rectype).
le_link_index
Eine Zeile pro (Link, Ziel) in einem Datensatz — das Fundament für die geplante Backlink-Funktion.
Spalte
Typ
Hinweise
id
int, PK (auto)
file
int
Baum-ID
xref
varchar(20)
rectype
varchar(15)
tag_path
varchar(255)
wo der Link im Datensatz sitzt
link_class
varchar(10)
xref / ext / pic / classic / other
token
text
das rohe Link-Token
snippet
text, nullable
zum Build-Zeitpunkt erfasster Anzeigekontext
target_xref
varchar(20), nullable
XREF-Link-Ziel
target_tree
varchar(255), nullable
target_uid
varchar(255), nullable, index
UID-Link-Ziel (per Längen-Heuristik befüllt)
Indizes auf (file, xref, rectype), target_xref und target_uid.
le_uid_index
Eine Zeile pro in einem Datensatz gefundenem UID-Tag (Datensatz- und Fakt-Ebene). Kein Unique-Constraint — UIDs sind nicht eindeutig garantiert.
Spalte
Typ
Hinweise
id
int, PK (auto)
file
int
Baum-ID
xref
varchar(20)
uid
varchar(255), index
UID-Wert wörtlich (ohne Normalisierung)
rectype
varchar(15)
tag_path
varchar(255)
hash
varchar(32)
MD5-Fingerprint (inkrementelle Änderungsprüfung)
Indizes auf uid und (file, xref, rectype).
le_index_meta
Eine einzelne Zeile (id = 1) mit dem Frische-Zustand von beiden — dem Link-Index und dem UID-Index.
Spalte
Typ
Hinweise
id
int, PK, Default 1
einzelne Zeile
last_run
timestamp, nullable
letzter vollständiger Link-Index-Lauf
rows
int, Default 0
Zeilenzahl des Link-Index
uid_last_run
timestamp, nullable
letzter vollständiger UID-Index-Lauf
uid_rows
int, Default 0
Zeilenzahl des UID-Index
3.4 - Klassenhierarchie
Automatisch generierter Klassenüberblick
Automatisch generiert durch docs/tools/generate-class-hierarchy.php — nicht manuell bearbeiten.
Schwester-Repo unter modules_v4/wt-shared-libs (Composer-Path-Repo)
Lage
Das Modul liegt innerhalb einer webtrees-Installation unter modules_v4/linkenhancer/. Sein composer.json
zieht die Schwester-Bibliothek als lokales Path-Repository ein:
Hinweis:composer.json
setzt prepend-autoloader: false — der Autoloader des Moduls wird an den Autoloader von webtrees angehängt (nicht ersetzt).
Bind-Mount
Im aktuellen Setup wird auf Linux mit nativ installiertem Apache Webserver entwickelt. Der Quellbaum der PHP-Laufzeit wird über einen Bind-Mount der Repo-Ordner unterhalb von modules_v4 bereitgestellt. Die CLI-Tools und PHPStan müssen vom Mount-Pfad aus ausgeführt werden (den Pfad, den die webtrees-Instanz sieht), nicht vom Quellpfad, damit app/ und vendor/autoload.php richtig auflösen:
cd /path/to/webtrees/modules_v4/linkenhancer
composer analyse
Gulp-Tasks für JS/CSS-Bundling, Übersetzungen und Versionierung
Bundling, Übersetzungs-Kompilierung und Versionierung werden über Gulp-Tasks in gulpfile.mjs
gesteuert, aufgerufen mit npx gulp <task>. Die npm run *-Skripte decken nur das Testing ab — siehe die Seite Testing.
ergänzt Issue-Referenzen in Commit-Messages (braucht FORGEJO_TOKEN in .env) - wurde nur einmalig angewendet für die Übernahme des Repos von GitHub zu Codeberg
JS / CSS Bundling
build = clean → (parallel) JS + CSS.
clean löscht zunächst resources/js/bundle* und resources/css/bundle*.
JS (Rollup): zwei Entry-Bundles — jsIndex (das Haupt-index-le.js
) und jsExtractLeConfig (le-config.js
). Ausgabe nach resources/js/bundle*.
CSS (PostCSS): Dateibasierte Subtasks aus getCssSubTasks(), mit autoprefixer + cssnano. Ausgabe nach resources/css/bundle*.
Dev-Mode (devbuild) schaltet per toggleDevMode(true) einen Flag um — er deaktiviert das Minifizieren und erzeugt SourceMaps.
Build- & Archive-Pipeline
flowchart LR
subgraph B["🔧 npx gulp build"]
C["🧹 clean<br/>(bundle*)"]
J["`📜 Rollup<br/>**index-le.js** · le-config.js`"]
P["🎨 PostCSS<br/>autoprefixer · cssnano"]
C --> J
C --> P
end
subgraph A["📦 npx gulp archive"]
A1["build"]
A2["po2php"]
A3["`create-archive.sh<br/>**dist/linkenhancer_v<ver>.zip**`"]
A1 --> A2 --> A3
end
classDef bStyle fill:#e3f2fd,stroke:#1565C0,stroke-width:2px
classDef aStyle fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px
class C,J,P bStyle
class A1,A2,A3 aStyle
Übersetzungsdateien
updatepo regeneriert nur die Vorlage (messages.pot). Die .po-Dateien selbst werden über Weblate aktualisiert, nicht lokal, um Konflikte zu vermeiden — siehe die Seite Übersetzungs-Pipeline.
po2php wandelt die committeten .po-Dateien in die PHP-Arrays um, die webtrees zur Laufzeit lädt.
3.7 - Übersetzungs-Pipeline
Wie übersetzbare Strings vom Quellcode bis zur Laufzeit fließen (xgettext → Weblate → PO → PHP)
Das Modul bringt seine eigenen Übersetzungen über die Standard-gettext-Kette ein, gehostet auf Codeberg Weblate.
Ablauf
flowchart TB
subgraph Extraction["1️⃣ Quellcode → POT"]
direction LR
PHP["📝 src/*.php"] -->|npx gulp updatepo| POT["📄 messages.pot"]
end
subgraph Translation["2️⃣ Übersetzung"]
direction LR
WEBLATE["🌐 Weblate"] -->|download| PO["🗝 <lang>.po"]
end
subgraph Rendering["3️⃣ Webtrees"]
direction LR
PHPARR["📦 lang.php"] -->|I18N| RENDER["✅ Rendering"]
end
POT -->|upload| Translation
PO -->|npx gulp po2php| Rendering
classDef sourceStyle fill:#e3f2fd,stroke:#1565C0,stroke-width:2px
classDef fileStyle fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px
classDef weblateStyle fill:#f3e5f5,stroke:#7B1FA2,stroke-width:2px
classDef renderStyle fill:#fce4ec,stroke:#C2185B,stroke-width:2px
class PHP sourceStyle
class POT,PO,PHPARR fileStyle
class WEBLATE weblateStyle
class RENDER renderStyle
Strings extrahieren
update-po-files.sh
führt xgettext über jede *.php / *.phtml aus, außer util/, vendor/, node_modules/ und tests/:
I18N::translate() / MoreI18N::translate() — modulspezifische Strings. translate ist ein xgettext-Keyword, daher werden diese aufgenommen.
MoreI18N::xlate() — Strings, die bereits vom webtrees-Core-POT abgedeckt sind. Gleiches Laufzeit-Verhalten, anderer Name, daher unsichtbar für xgettext. Nach einem Core-Update messages.pot mit dem Core-POT vergleichen und neu abgedeckte msgids auf xlate() umstellen.
.po-Dateien werden nicht lokal gemergt — das ist Weblates Aufgabe, um Konflikte zu vermeiden. Sobald Weblate aktualisierte .po-Dateien liefert, kompilieren:
npx gulp po2php
Hinweis: ein früherer .po → .mo-Schritt ist überflüssig (Formatwechsel bei den Übersetzungen, Issue #88).
3.8 - WTHB-TOC-Language-Pipeline
Wie das Inhaltsverzeichnis des deutschen webtrees-Handbuchs erzeugt wird (GenWiki → pywikibot → Generierung → externes Repo / Pages / Weblate → Modul)
Überblick
Das Paket linkenhancer-wthb-toc-lang stellt das Inhaltsverzeichnis (TOC) des deutschen webtrees-Handbuchs — samt Übersetzungen — der WTHB-Hilfefunktion zur Verfügung. Die Produktion ist ein Rundlauf, der komplett außerhalb der webtrees-Installation läuft: Er liest das Handbuch von GenWiki, erzeugt das TOC neu, schreibt das Ergebnis als MediaWiki-Template zurück nach GenWiki und — parallel — die strukturierten Daten in ein externes Codeberg-Repo, das Codeberg Pages, Weblate (Übersetzungen) und schließlich das Modul versorgt. Diese Seite dokumentiert die Produktionsseite; die Konsumenten-Seite (Cache, CLI, Rendering) steht auf der WTHB-Funktionsseite und der Seite CLI-Skripte.
Der Rundlauf hat zwei Hälften: den GenWiki-Rundlauf (Handbuch lesen → generieren → TOC-Template zurück schreiben) und die Delivery-Pipeline (strukturierte Daten → externes Repo → Pages / Weblate → Modul).
Template zurück nach GenWiki schreiben (pywikibot WRITE)
gerendertes Template per Parse-Check prüfen (curl + jq)
JSON in das externe Repo deployen
nachgelagert: Repo-CI, Weblate und Modul holen die Daten
GenWiki-Datenabfrage (pywikibot READ)
Schritt 1 ist wt-maintain.py, ein pywikibot-Bot (SectionsExportBot, erbt von ExistingPageBot). Er ruft site.login() auf, iteriert allpages(prefix="Webtrees Handbuch") und fragt pro Seite die MediaWiki-API (action=parse&prop=sections) plus imagelinks() (FilePage-Info) ab. Ergebnis sind drei CSV-Dateien:
wthb-pageinfo.csv,
wthb-sections.csv und
wthb-images.csv.
Zwei manuelle Nacharbeiten bleiben (laut Skript-Header): (b) das Bilderverzeichnis pflegen und (c) die Bilder in die Kategorie:Webtrees legen — von Hand (Helfer: wt-webtrees-cat-files.sh).
TOC-Generierung — wt-gen-toc.py
Schritt 2 ist ein pandas-Skript, das den IST-Zustand (die drei CSVs) mit der SOLL-Zielstruktur in wthb-pageinfo_ext.csv kombiniert. classify_prefix() vergibt Kapitel-/Abschnitts-Präfixe. Es schreibt vier Outputs:
wthb-toc.wiki — die MediaWiki-TOC-Quelle
wthb-toc.json — die strukturierten Daten, die das Modul verbraucht
de.json — die deutsche Sprachdatei für Weblate
wthb-toc.html — eine gerenderte HTML-Kopie (legacy/parallel — der aktuelle Konsument rendert aus JSON)
GenWiki-Template (pywikibot WRITE)
Schritte 3–4 wickeln das generierte TOC im MediaWiki-Template Vorlage:WT-Verzeichnis-TOC ein und schreiben es zurück:
die pagefromfile-Datei kombiniert einen <noinclude>-Teil ({{Dokumentation}} + [[Kategorie:Webtrees]]) mit einem <includeonly>-Teil (Stand: <datum> + <div class="wthbtoc">…</div>).
pywikibot pagefromfile -force -pt:1 -summary:"Bot: Webtrees Handbuch Inhaltsverzeichnis aktualisiert" führt den Write aus.
Schritt 5 verifiziert das Ergebnis, indem es das Template über die GenWiki-API (action=parse&format=json) neu parst und das HTML mit jq -r '.parse.text["*"]' extrahiert.
Externes Repo, CI & Pages
Die zweite Hälfte der Pipeline (ab Schritt 6) befördert die strukturierten Daten aus dem Host heraus:
flowchart TB
OUT["`📦 *Ausgaben*<br/>wthb-toc.json + de.json`"]
DEP["`🚀 **wt-deploy-toc-lang.sh**`"]
subgraph EXTERNAL["🌐 Externe Infrastruktur"]
REPO["`📦 **linkenhancer-wthb-toc-lang**<br/>(Codeberg)`"]
META["`🔧 *tools/gen_meta.py*<br/>languages.json + version.json`"]
CI["`⚙️ *CI regen-meta.yml*<br/>gen_toc_html.py`"]
PG["`🌐 **Codeberg Pages**`"]
WLB["`🌐 **Weblate** · linkenhancer/wthb-toc`"]
TNG["🗝 en.json · fr.json · …"]
end
subgraph MODULE["📦 Modul (linkenhancer)"]
SVC["`**WthbTocService**`"]
DS["`💾 *data/…/wthb-toc-lang/*<br/>2-Layer-Cache`"]
RDR["`📄 **help-wthb-toc.phtml**<br/>TOC-Modal`"]
end
OUT -->|"cp wthb-toc.json + de.json"| DEP
DEP -->|"git add / commit / push"| REPO
REPO -->|"regen meta"| META
REPO --> CI --> PG
REPO -->|"de.json (Source)"| WLB
WLB --> TNG
TNG -->|"[lang].json"| REPO
PG -->|"fetch JSON"| MODULE
SVC --> DS --> RDR
classDef coreStyle fill:#ede7f6,stroke:#5e35b1,stroke-width:2px
classDef okStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef queueStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px
class OUT okStyle
class DEP,REPO,META,CI,SVC coreStyle
class PG,TNG,DS,RDR okStyle
class WLB queueStyle
wt-deploy-toc-lang.sh kopiert wthb-toc.json und de.json in das Repo linkenhancer-wthb-toc-lang, führt tools/gen_meta.py aus (regeneriert languages.json + version.json) und macht git add/commit/push. Es ist standalone aufrufbar und skippt weich (Warnung + exit 0), wenn Repo oder JSON fehlen, sodass es den set -e-Orchestrator nie abbricht.
Repo-CI (regen-meta.yml) führt gen_toc_html.py aus und veröffentlicht auf Codeberg Pages.
Weblate
Die de.json im Repo ist die Quelle der Weblate-Komponente wt-modules/linkenhancer-wthb-toc; die übrigen [lang].json-Dateien sind deren Übersetzungs-Ziele. Die Übersetzungen fließen zurück ins Repo und werden über Codeberg Pages ausgeliefert.
rsync das Modul nach dist/linkenhancer/ und schließt alles aus, das nicht ausgeliefert wird (unten).
Erzeugt dist/linkenhancer_v<version>.zip.
Aus dem Archiv ausgeschlossen
create-archive.sh lässt unter anderem aus: node_modules/, .git*, gulpfile.mjs, package*.json, phpstan*, tests/, die util/*-Buildskripte, resources/js/src, resources/js/tests, resources/img/, resources/lang/*.po / *.mo und .env. Ausgeliefert werden nur die gebauten bundle*-JS/CSS, die kompilierten resources/lang/*.php, src/, resources/views/, resources/css/ und die Modul-Entry-Dateien.
Veröffentlichen
Das Release in git taggen.
dist/linkenhancer_v<version>.zip mit Release-Notizen zu einem Codeberg-/GitHub-Release hochladen.
Das Archiv wird nicht ins Repo committet — es wird on demand erzeugt und dem Release beigefügt.