Das ist eine für den Ausdruck optimierte Ansicht des gesamten Kapitels inkl. Unterseiten. Druckvorgang starten.

Zur Standardansicht zurückkehren.

LinkEnhancer

webtrees Custom-Modul: Querverweise, erweiterte Links, Markdown-Editor und kontextsensitive Hilfe

License: GPL v3 webtrees major version

Notiz mit LinksSeitentitel als Link

Kleines Menü mit Hilfe-Link
Notiz mit erweiterten Links und
Bild aus Mediendatensatz
Seitentitel als Home-Link
kleines Menü mit Hilfe-Link
Markdown-EditorMarkdown-Hilfe
Markdown-EditorMarkdown-Hilfe

Was dieses Modul tut

Dieses Modul bündelt einige Beispiele aus dem deutschen Webtrees-Handbuch und verbessert die Anwendung dieser Funktionen — jede Komponente kann individuell aktiviert werden.

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.

Manuelle Installation

  1. Laden Sie die neueste Version des Moduls herunter.
  2. Laden Sie die heruntergeladene Datei auf Ihren Webserver hoch.
  3. Entpacken Sie das Paket in Ihr modules_v4-Verzeichnis.
  4. Benennen Sie den Ordner in linkenhancer um.

Wenn alles erfolgreich war, sollten Sie im modules_v4-Verzeichnis einen Unterordner linkenhancer mit dem entpackten Inhalt sehen.

Hinweis: Bei der ersten Installation kann folgendes Problem auftreten: “PDO error - There is no active transaction” — Details siehe bekannte Probleme.

1.3 - Admin-Einstellungen

Webtrees Modul-Einstellungen

Im oberen Bereich der Admin-Seite ist es möglich auf die administrative Querverweis-Übersicht und die Funktion xref_IDs neu nummerieren (mit Verweisen) zuzugreifen.
Ferner können die Grundfunktionen (de-)aktiviert werden:

  1. Erweiterte Links
  2. Markdown Verbesserungen
  3. Hilfsfunktionen (Webtrees Handbuch)
  4. Home-Link
  5. UID-Abfrage

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:

Admin-Seite

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
private function toggleTinyMde(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:

[3] Webtrees Handbuch

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.

Admin-Seite Datentabelle

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-Tabelle le_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:

  1. path
  2. handler
  3. 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.

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
  • url: Link zur Web-Ressource (String, Pflichtfeld)
  • class: zusätzliche CSS-Klassennamen (String, optional)
  • 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)

Beispiel:

[
  {"title":"webtrees FAQ <i class=\"fa-solid fa-circle-question\"></i>",
   "url":"https://webtrees.net/faq/",
   "i18n": { "de": "webtrees Häufig-gestellte-Fragen <i class=\"fa-solid fa-circle-question\"></i>" }
  }
]

Dynamische CSS-Stile können mit einem JSON-Objekt konfigurieren werden:

{ 
  "*": ".homelink { color: #039; }",
  "colors_nocturnal": ".homelink { color: antiquewhite; }"
}

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&paramN), 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:

Notiz mit erweiterten Links - Bearbeiten und Ansicht

2.1.1 - Querverweis-Übersicht

Querverweis-Übersicht mit Link-Index für große Bäume

Es gibt drei Arten von Übersichten:

  1. Übersicht über alle Daten für den Admin (Admin-Übersicht),
  2. auf einen Stammbaum begrenzte Übersicht (Benutzer-Übersicht) und
  3. 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.

Admin overview

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.

Record overview

Unterstützte Engines

Live-ÜbersichtLink-Index (CLI)
MariaDB / MySQLja (REGEXP-Gate)ja
PostgreSQLja (REGEXP-Gate)ja
SQLite / SQL ServerEingeschränkter Modus — automatischer Fallback auf ein grobes LIKE-Gate + Volltabellen-Scan pro Anfrage (Banner auf der Seite); die exakte Klassifizierung erfolgt weiterhin in PHPnicht 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

Der Index wird durch build-link-index.php gepflegt:

  • 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):

*/30 * * * * cd /path/to/webtrees && php modules_v4/linkenhancer/cli/build-link-index.php --limit=2000 >> /var/log/linkenhancer-index.log 2>&1

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.

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:

  1. Den Stammbaum mit XREF-Kollisionen auswählen (doppelte XREFs mit anderen Bäumen).
  2. Die Seite zeigt jede kollidierende XREF, ihren Record-Typ und die Anzahl der eingehenden LinkEnhancer-Referenzen aus dem Link-Index.
  3. 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
  4. Jeder umnummerierte Datensatz wird im Edit-Log protokolliert (Admin → Änderungslog), z.B. Renumbered @I456@ → @I457@ (INDI, 3 core rows, 2 links).
  5. 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:

KorrekturWas sie tut
XREF ⇄ UID Referenz-TauschKonvertiert 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 korrigierenSetzt, 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 meldenNur Bericht: Listet LinkEnhancer-Links auf, deren Ziel-Datensatz nicht mehr existiert. Es werden keine Änderungen vorgenommen.
Mehrdeutige UID-Ziele meldenNur Bericht: Listet Links auf, deren UID-Ziel auf mehr als einen Datensatz auflöst. Es werden keine Änderungen vorgenommen.
Doppelte UIDs im Index meldenNur 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-Logo

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.

Zu Markdown siehe auch:

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

Notiz mit Links

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:

  • ![Alternativtext für GEDCOM-Objekt](#@id=@M1@)
  • ![Alternativtext für Bild im öffentlichen Ordner](#@public=webtrees.png "Titel")
  • ![Bild mit definierter Höhe, Breite und zusätzlicher CSS-Klasse](#@id=@M1@&h=200&w=200&cname=float-right)

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

Man kann einen visuellen Markdown-Editor (mde) für Notiz-Textareas aktivieren. Unter der Haube wird das Projekt TinyMDE - A tiny, dependency-free embeddable HTML/JavaScript Markdown editor verwendet.

Markdown-Editor

Neben der Syntax-Hervorhebung wird eine Icon-Leiste für gängige Formatierungsbefehle, ein Hilfe-Popup und Zeilennummerierung mitgeliefert.

Markdown-Hilfe

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.

2.2.3 - Markdown-Erweiterungen

Durchstreichung, Beschreibungslisten, Fußnoten, Hervorhebung, TOC, Smart Punctuation

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.

Markdown Tabellen-Zellenhöhe

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
CompGen-Logo

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.

Kopfmenü mit Hilfe-Link

WTHB Subkontext-Hilfe

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.

WTHB Benutzer-Einstellung

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.

WTHB Volltextsuche und TOC

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.

WTHB Untermenü und aufgeteilter Menüeintrag

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.

Seitentitel als Link

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):

*/10 * * * * cd /path/to/webtrees && php modules_v4/linkenhancer/cli/build-uid-index.php --limit=5000 >> /var/log/linkenhancer-uid-index.log 2>&1

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.

#BeschreibungGilt für Version
P001Backlink für Level-1-gemeinsame Notizen #5181 — nicht mehr notwendig, es sei denn, man möchte einen zusätzlichen Edit-Link. app/Fact.php2.2.1 - 2.2.6
P002headContent/bodyContent für dieses Modul im Admin-Bereich aktivieren, um den Kontext-Hilfe-Link anzuzeigen #5214. resources/views/layouts/administration.phtml2.2.1 - 2.2.6
P003Datensatz hat mehrere uid-Felder #4828. app/Services/GedcomEditService.php2.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.

SkriptZweckBraucht eine webtrees-Installation + DB?
cli/build-link-index.php [--limit=N] [--tree=<id>] [--rebuild] [--flush]baut/aktualisiert den Link-Index für die XREF-Übersichtja
cli/build-uid-index.php [--limit=N] [--tree=<id>] [--rebuild] [--flush] [--force]baut/aktualisiert den UID-Index für die UID-Sucheja
cli/refresh-wthb-toc.php [--all] [--lang=<list>] [--clear] [--force]aktualisiert den WTHB-Inhaltsverzeichnis-Cache (Produktion)ja
test-text-tag-collector.phpeigenständige Unit-Tests für den Text-Tag-Collectornein
test-link-classifier.phpeigenständige Unit-Tests für den Link-Klassifizierer (inkl. Ziel-Extraktion)nein
test-uid-tag-collector.phpeigenständige Unit-Tests für den UID-Tag-Collectornein
test-uid-index-service.phpeigenständige Unit-Tests für den UID-Index-Servicenein
tests/smoke-text-tag-collector.php [limit]scannt Datensätze mit erweiterten Links, gibt jeden erfassten Wert ausja
smoke-migration5.phpSQLite-Smoke-Test für Migration5 (UID-Index-Schema)ja
tests/p1-measure.php [--tree=<id>]Read-only-Scaling-Messung für die XREF-Übersichtja

Ausführen vom webtrees-Root aus mit der gleichen PHP-Version, mit der die Instanz läuft (webtrees benötigt PHP 8.3+):

php modules_v4/linkenhancer/tests/test-text-tag-collector.php
php modules_v4/linkenhancer/tests/smoke-text-tag-collector.php 10

Cron-Beispiel (alle 2 Stunden, 500 Datensätze pro Lauf):

15 */2 * * * cd /path/to/webtrees && php modules_v4/linkenhancer/cli/<name>.php --limit=500 >> /var/log/linkenhancer-<name>.log 2>&1

3.2 - Mitwirken

So trägt man zum LinkEnhancer-Modul bei

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.

Neben Englisch sind folgende Sprachen verfügbar:

  • Katalanisch (von Bernat Josep Banyuls i Sala)
  • Niederländisch (von TheDutchJewel)
  • Spanisch (von Bernat Josep Banyuls i Sala)
  • Deutsch

Repository

Primäres Repository: https://codeberg.org/bschwede/linkenhancer

Die gesamte Aktivität findet auf Codeberg statt:

Mirror-Repository: https://github.com/bschwede/linkenhancer

3.3 - Projektstruktur

Verzeichnislayout, Abhängigkeiten und Datenbank-Schema

Verzeichnislayout

linkenhancer/
├── module.php            ← webtrees-Entry-Point
├── autoload.php          ← PSR-4-Autoloader
├── cron-jobs.php         ← Self-Registration-Manifest für cronjob
├── latest-version.txt    ← aktuelle Version (mit LinkEnhancerModule.php synchron)
├── composer.json         ← PHP-Abhängigkeiten (wt-shared-libs, phpstan)
├── package.json          ← JS-Abhängigkeiten (gulp, rollup, mocha)
├── gulpfile.mjs          ← Build-Tasks
├── phpstan.neon          ← Konfiguration der statischen Analyse
├── phpstan-bootstrap.php ← PSR-4-Bootstrap für PHPStan
├── phpstan-baseline.neon ← bekannte Probleme
├── src/                  ← PHP (PSR-4)
│   ├── LinkEnhancerModule.php
│   ├── LinkEnhancerModuleExt.php
│   ├── LinkEnhancerUtils.php
│   ├── SettingInterface.php
│   ├── CommonMark/       ← CommonMark-Extensionen
│   ├── DataFix/          ← Datenkorrektur-Handler + Dispatcher
│   ├── Factories/        ← CustomMarkdownFactory
│   ├── Http/             ← Request-Handler
│   ├── Schema/           ← Migrations + SeedHelpTable
│   └── Services/         ← Geschäftslogik
├── resources/
│   ├── js/               ← JS-Quellcode (src/) + Build-Ausgabe (bundle-*)
│   ├── css/              ← CSS-Quellcode + Build-Ausgabe
│   ├── lang/             ← .pot / .po / .php (Übersetzungen)
│   ├── views/            ← PHTML-Views
│   └── img/              ← Screenshots
├── cli/                  ← CLI-Wartungsskripte
├── tests/                ← Standalone- + Smoke-Tests
└── util/                 ← Build-/Wartungs-Shell-Skripte

Abhängigkeiten

Datenbank-Schema

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.

SpalteTypHinweise
idint, PK (auto)
pathvarchar(255)Routen-Pfad
handlervarchar(150)vollqualifizierte Handler-Klasse
handler_keyvarchar(150), indexversionsneutrale Key (2.2.6 + 2.3 kompatibel)
methodvarchar(20)
extrasvarchar(60)
subcontextvarchar(250)
categoryvarchar(60)standard / custom module / generic …
orderint, Default 10
urlvarchar(250)
updated_attimestamp, 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.

SpalteTypHinweise
fileintBaum-ID (Teil des PK)
xrefvarchar(20)(Teil des PK)
rectypevarchar(15)GEDCOM-Tag, z.B. INDI, OBJE (Teil des PK)
hashvarchar(32)MD5 des Datensatztextes
scanned_attimestamp, nullable

Primärschlüssel (file, xref, rectype).

Eine Zeile pro (Link, Ziel) in einem Datensatz — das Fundament für die geplante Backlink-Funktion.

SpalteTypHinweise
idint, PK (auto)
fileintBaum-ID
xrefvarchar(20)
rectypevarchar(15)
tag_pathvarchar(255)wo der Link im Datensatz sitzt
link_classvarchar(10)xref / ext / pic / classic / other
tokentextdas rohe Link-Token
snippettext, nullablezum Build-Zeitpunkt erfasster Anzeigekontext
target_xrefvarchar(20), nullableXREF-Link-Ziel
target_treevarchar(255), nullable
target_uidvarchar(255), nullable, indexUID-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.

SpalteTypHinweise
idint, PK (auto)
fileintBaum-ID
xrefvarchar(20)
uidvarchar(255), indexUID-Wert wörtlich (ohne Normalisierung)
rectypevarchar(15)
tag_pathvarchar(255)
hashvarchar(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.

SpalteTypHinweise
idint, PK, Default 1einzelne Zeile
last_runtimestamp, nullableletzter vollständiger Link-Index-Lauf
rowsint, Default 0Zeilenzahl des Link-Index
uid_last_runtimestamp, nullableletzter vollständiger UID-Index-Lauf
uid_rowsint, Default 0Zeilenzahl des UID-Index

3.4 - Klassenhierarchie

Automatisch generierter Klassenüberblick

Automatisch generiert durch docs/tools/generate-class-hierarchy.php — nicht manuell bearbeiten.

Modul

  • LinkEnhancerModule extends AbstractModule implements MiddlewareInterface, ModuleCustomInterface, ModuleGlobalInterface, ModuleConfigInterface, ModuleDataFixInterface, ModuleListInterface, ModuleTabInterface, SettingInterface — LinkEnhancerModule.php
  • SettingInterface — SettingInterface.php
  • LinkEnhancerUtils — LinkEnhancerUtils.php
  • LinkEnhancerModuleExt extends LinkEnhancerModule implements ModuleExtGlobalInterface — LinkEnhancerModuleExt.php

CommonMark (Extensions)

  • LeImageRenderer (final) implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface — CommonMark/LeImageRenderer.php
  • SmartPunctHelper — CommonMark/SmartPunctHelper.php
  • LeTableOfContentsRenderer (final) implements NodeRendererInterface, XmlNodeRendererInterface — CommonMark/LeTableOfContentsRenderer.php

Factories

  • CustomMarkdownFactory extends MarkdownFactory — Factories/CustomMarkdownFactory.php

Request-Handler

  • DataFixBlocksAction implements RequestHandlerInterface — Http/RequestHandlers/DataFixBlocksAction.php
  • HelpWthbAction implements RequestHandlerInterface — Http/RequestHandlers/HelpWthbAction.php
  • XrefDetailData (final) implements RequestHandlerInterface — Http/RequestHandlers/XrefDetailData.php
  • XrefOverviewListData (final) implements RequestHandlerInterface — Http/RequestHandlers/XrefOverviewListData.php
  • GotoIdTreeAction extends GotoIdAction — Http/RequestHandlers/GotoIdTreeAction.php
  • GotoUidTreeAction extends GotoUidAction — Http/RequestHandlers/GotoUidTreeAction.php
  • GotoXrefAction implements RequestHandlerInterface — Http/RequestHandlers/GotoXrefAction.php
  • DataFixRebuildIndexAction implements RequestHandlerInterface — Http/RequestHandlers/DataFixRebuildIndexAction.php
  • RenumberActionHandler (final) — Http/RequestHandlers/RenumberActionHandler.php
  • HelpMdAction implements RequestHandlerInterface — Http/RequestHandlers/HelpMdAction.php
  • AdminXrefOverviewData (final) implements RequestHandlerInterface — Http/RequestHandlers/AdminXrefOverviewData.php
  • GotoIdAction extends AbstractGotoAction — Http/RequestHandlers/GotoIdAction.php
  • HelpWtCoreAction implements RequestHandlerInterface — Http/RequestHandlers/HelpWtCoreAction.php
  • WthbAdminHandler (final) — Http/RequestHandlers/WthbAdminHandler.php
  • GotoUidAction extends AbstractGotoAction — Http/RequestHandlers/GotoUidAction.php
  • AbstractGotoAction (abstract) implements RequestHandlerInterface — Http/RequestHandlers/AbstractGotoAction.php

Schema

  • SeedHelpTable implements SeedInterface — Schema/SeedHelpTable.php
  • Migration6 implements MigrationInterface — Schema/Migration6.php
  • Migration2 implements MigrationInterface — Schema/Migration2.php
  • Migration7 implements MigrationInterface — Schema/Migration7.php
  • Migration3 implements MigrationInterface — Schema/Migration3.php
  • Migration4 implements MigrationInterface — Schema/Migration4.php
  • Migration1 implements MigrationInterface — Schema/Migration1.php
  • Migration5 implements MigrationInterface — Schema/Migration5.php
  • Migration0 implements MigrationInterface — Schema/Migration0.php

Services

  • AdminSettingsBuilder (final) — Services/AdminSettingsBuilder.php
  • MarkdownEditorActivationService (final) — Services/MarkdownEditorActivationService.php
  • IndexRebuildScheduler (final) — Services/IndexRebuildScheduler.php
  • UidTagCollector (final) — Services/UidTagCollector.php
  • TextTagCollector (final) — Services/TextTagCollector.php
  • ContentBuilder (final) — Services/ContentBuilder.php
  • RenumberWithLinksService (final) — Services/RenumberWithLinksService.php
  • DataFiles (final) — Services/DataFiles.php
  • XrefOverviewColumns (final) — Services/XrefOverviewColumns.php
  • LinkIndexService (final) — Services/LinkIndexService.php
  • XrefsService (final) — Services/XrefsService.php
  • XrefDetailService (final) — Services/XrefDetailService.php
  • WthbService — Services/WthbService.php
  • IdResolver (final) — Services/IdResolver.php
  • LinkRenderer (final) — Services/LinkRenderer.php
  • WthbTocService (final) — Services/WthbTocService.php
  • UidIndexService (final) — Services/UidIndexService.php

DataFix

  • FixHandlerInterface — DataFix/FixHandlerInterface.php
  • DataFixDispatcher — DataFix/DataFixDispatcher.php
  • TreeLookupTrait — DataFix/TreeLookupTrait.php
  • DanglingLinksFix (final) implements FixHandlerInterface — DataFix/Handlers/DanglingLinksFix.php
  • DuplicateUidFix (final) implements FixHandlerInterface — DataFix/Handlers/DuplicateUidFix.php
  • WtTypeFix (final) implements FixHandlerInterface — DataFix/Handlers/WtTypeFix.php
  • XrefUidSwapFix (final) implements FixHandlerInterface — DataFix/Handlers/XrefUidSwapFix.php
  • AmbiguousUidFix (final) implements FixHandlerInterface — DataFix/Handlers/AmbiguousUidFix.php

3.5 - Entwicklungsumgebung

Einrichtung einer lokalen Entwicklungsumgebung

Was man zum Bauen und Testen des Moduls lokal braucht.

Voraussetzungen

KomponenteVersionHinweise
PHP8.3+dieselbe Version, mit der die webtrees-Instanz läuft
Composer2.xinstalliert wt-shared-libs + phpstan
Node.js18+ (LTS)für Gulp / Rollup / Mocha
webtrees2.2.x oder 2.3.xnötig für PHPStan (app/-Klassen) und die Smoke-Tests
wt-shared-libsneueste VersionSchwester-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:

"repositories": [
    { "type": "path", "url": "../wt-shared-libs", "options": { "symlink": false } }
]

Einrichtung

# PHP-Abhängigkeiten (installiert wt-shared-libs + phpstan)
composer install

# JS-Abhängigkeiten (gulp, rollup, mocha, …)
npm ci

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

Die genauen Befehle stehen auf den Seiten CLI-Skripte und PHPStan.

3.6 - Build-System

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.

Tasks

TaskBefehlWirkung
buildnpx gulp buildclean → JS (Rollup) + CSS (PostCSS)
devbuildnpx gulp devbuildwie build, aber Dev-Mode (kein Minify, SourceMaps)
updateponpx gulp updatepoführt update-po-files.sh aus → xgettext über src/ → messages.pot
po2phpnpx gulp po2phpkompiliert resources/lang/*.po → resources/lang/*.php
syncversionnpx gulp syncversionhält latest-version.txt ↔ LinkEnhancerModule.php synchron
bumpversionnpx gulp bumpversionerfordert einen sauberen git-Tree → Version-Bump → syncversion
archivenpx gulp archivebuild + po2php + create-archive.sh (Release-ZIP)
forgejo:issue-refsnpx gulp forgejo:issue-refs[:dry]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/:

xgettext -L PHP \
  --keyword=translate --keyword=plural:1,2 --keyword=translateContext:1c,2 \
  --add-comments=I18N --from-code=utf-8 \
  --output=resources/lang/messages.pot …

Zwei Marker-Helfer steuern, was extrahiert wird:

  • 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.

/* I18N: … */-Kommentare geben Übersetzern Kontext (über --add-comments=I18N aufgenommen).

PO → PHP

.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.

Ressourcen

Pipeline

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).

Der GenWiki-Rundlauf:

flowchart TB
    subgraph HOST["🖥️ Host · gw-skripte/"]
        ORCH["`🚦 **wt-maintain.sh**<br/>*Orchestrierung*`"]
        PREAD["`🤖 **pywikibot** · *wt-maintain.py*<br/>SectionsExportBot — *READ*`"]
        GEN["`⚙️ **wt-gen-toc.py** *(pandas)*`"]
        PREP["`📄 *pagefromfile-Datei*<br/>vorlage_wt-verzeichnis-toc.txt`"]
        PWRITE["`🤖 **pywikibot** · *pagefromfile*<br/>*WRITE* — Bot-Zusammenfassung`"]
    end

    GW["`📖 **GenWiki** · wiki.genealogy.net<br/>*Webtrees-Handbuch-Seiten*`"]
    CSV["`📄 *3 CSV*<br/>pageinfo · sections · images`"]
    OUT["`📦 *Ausgaben*<br/>wthb-toc.wiki · .html · .json · de.json`"]
    TPL["`📖 **Vorlage:WT-Verzeichnis-TOC**<br/>*noinclude + includeonly TOC*`"]
    API["`🔍 *API action=parse* → HTML<br/>(curl + jq)`"]

    GW -->|"allpages prefix 'Webtrees Handbuch'<br/>API parse/sections + imagelinks"| PREAD
    ORCH -.->|"drives"| PREAD
    PREAD --> CSV
    CSV -->|"+ wthb-pageinfo_ext.csv (target)"| GEN
    GEN --> OUT
    OUT --> PREP
    PREP --> PWRITE
    PWRITE -->|"pagefromfile -force"| TPL
    TPL -->|"curl API parse"| API

    classDef srcStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    classDef coreStyle fill:#ede7f6,stroke:#5e35b1,stroke-width:2px
    classDef okStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
    classDef pwbStyle fill:#fff8e1,stroke:#f57f17,stroke-width:2px
    classDef targetStyle fill:#fce4ec,stroke:#ad1457,stroke-width:2px

    class GW srcStyle
    class ORCH,GEN coreStyle
    class CSV,OUT,API okStyle
    class PREAD,PWRITE pwbStyle
    class TPL targetStyle

Orchestrierung — wt-maintain.sh

Alles läuft von einem Host-Skript (in gw-skripte/):

wt-maintain.sh <wthb-pageinfo_ext.csv> <DATADIR>

Es benötigt setenv.sh, das PWB_PY, PWB_CONF und SCRIPTDIR exportiert, und steuert diese Schritte der Reihe nach:

  1. GenWiki-Daten abfragen (pywikibot READ)
  2. TOC generieren
  3. pagefromfile-Wrapper-Datei bauen
  4. Template zurück nach GenWiki schreiben (pywikibot WRITE)
  5. gerendertes Template per Parse-Check prüfen (curl + jq)
  6. JSON in das externe Repo deployen
  7. 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:

  1. wthb-pageinfo.csv,
  2. wthb-sections.csv und
  3. 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.

Konsument (Modul)

Knapp — Details auf der WTHB-Funktionsseite und der Seite CLI-Skripte:

Konfiguration & Voraussetzungen

  • setenv.sh — exportiert PWB_PY, PWB_CONF, SCRIPTDIR.
  • pywikibot-Config — user-config-test.py / user-config-prod.py.
  • wthb-toc-lang.env — TOC_LANG_REPO=$HOME/dev/linkenhancer-wthb-toc-lang, TOC_LANG_DATADIR=$HOME/dev/wthb-toc-datadir.
  • Voraussetzungen: pywikibot, pandas, jq, python3.

Ops & Caveats

  • Manuelle Bild-Nacharbeit — Bilderverzeichnis und Kategorie:Webtrees müssen von Hand gepflegt werden.
  • pagefromfile-Login — laut Code-Kommentar muss ggf. site.login() in pagefromfile.py eingefügt werden.
  • Soft-Skip im Deploy — fehlendes Repo oder JSON ist eine Warnung, kein Fehler (hält set -e sicher).

3.9 - Testing

Test-Kette: PHP-Standalone, PHPStan und JS (Mocha)

Die komplette Kette läuft mit:

npm test

Das führt test:php → analyse:php → test:js aus, wobei jeder Schritt bestehen muss, bevor der nächste läuft.

JavaScript (Mocha + jsdom)

npm run test:js
# mocha 'resources/js/tests/**/*.test.js' --require jsdom-global/register

PHP-Standalone-Unit-Tests

npm run test:php
# führt jedes tests/test-*.php mit dem System-PHP aus — ohne webtrees, ohne DB
TestAbgedeckt
test-text-tag-collector.phpText-Tag-Collector
test-link-classifier.phpLink-Klassifizierer (inkl. Ziel-Extraktion)
test-uid-tag-collector.phpUID-Tag-Collector
test-uid-index-service.phpUID-Index-Service
test-i18n-mark.phpi18n-Marker
test-wthb-toc-service.phpWTHB-TOC-Service

PHP-Statische Analyse

npm run analyse:php      # oder: composer analyse
# PHPStan Level 6 — siehe die PHPStan-Seite

Smoke-Tests (brauchen webtrees + DB)

Diese üben eine echte Installation und sind nicht Teil von npm test:

php tests/smoke-text-tag-collector.php [limit]
php tests/smoke-migration5.php
php tests/p1-measure.php [--tree=<id>]

3.10 - PHPStan

Einrichtung und Konfiguration der statischen Analyse

Benötigt wird: PHPstan

Die statische Analyse ist über phpstan.neon konfiguriert (Level 6, Baseline in phpstan-baseline.neon).

Ausführen

composer analyse

Die Analyse benötigt eine webtrees-Installation (für die app/-Klassen + vendor/autoload.php). Ausführen vom bind-mounted-Pfad:

cd /path/to/webtrees/modules_v4/linkenhancer
composer analyse

Konfiguration

  • Level: 6
  • Baseline: phpstan-baseline.neon (bekannte Probleme)
  • Bootstrap: phpstan-bootstrap.php registriert die PSR-4-Namespaces direkt (umgeht den versionsgeprüften Autoloader in wt-shared-libs)
  • Scan-Verzeichnisse: ../../app/ (webtrees-Core-Klassen)
  • Ausgeschlossene Pfade: LinkEnhancerModuleExt.php (Verwendung im Zusammenhang mit Vesta-Modulen)
  • Speichergrenze: 1G
  • Parallel: deaktiviert (Single-Prozess, zuverlässiger in CI)

Wichtige Einstellungen

  • treatPhpDocTypesAsCertain: false — verhindert False Positives aus @var-Assertions
  • ignoreErrors für:
    • AccessLevel Enum (nur-Klasse in WT 2.3, in der 2.2-Laufzeit nicht verfügbar)
    • DB::prefix('') — beabsichtigt (Präfix ohne Identifier holen)

Baseline neu generieren

Nach beabsichtigten Änderungen, die neue (akzeptable) Fehler einführen:

vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=1G --generate-baseline=phpstan-baseline.neon

Hinweis: Dies über den direkten vendor/bin/phpstan-Aufruf ausführen — der composer-Script-Wrapper leitet zusätzliche Argumente nicht weiter.

3.11 - Release

Version-Bump, Build und Erstellung des Release-Archivs

Schritte

# 1. Version erhöhen (erfordert einen sauberen git-Tree)
npx gulp bumpversion

# 2. Build + Übersetzungen kompilieren + Release-Archiv erstellen
npx gulp archive

Was archive tut

npx gulp archive führt build → po2php → create-archive.sh aus. Das Skript:

  1. Liest die Version aus latest-version.txt .
  2. rsync das Modul nach dist/linkenhancer/ und schließt alles aus, das nicht ausgeliefert wird (unten).
  3. 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

  1. Das Release in git taggen.
  2. 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.