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

Zur Standardansicht zurückkehren.

Entwicklung

Entwicklungs-Dokumentation

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

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

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

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.

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.

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

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

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

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.

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.