Das ist eine für den Ausdruck optimierte Ansicht des gesamten Kapitels inkl. Unterseiten. Druckvorgang starten.
Entwicklung
1 - CLI-Skripte
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.
| Skript | Zweck | Braucht eine webtrees-Installation + DB? |
|---|---|---|
cli/build-link-index.php [--limit=N] [--tree=<id>] [--rebuild] [--flush] | baut/aktualisiert den Link-Index für die XREF-Übersicht | ja |
cli/build-uid-index.php [--limit=N] [--tree=<id>] [--rebuild] [--flush] [--force] | baut/aktualisiert den UID-Index für die UID-Suche | ja |
cli/refresh-wthb-toc.php [--all] [--lang=<list>] [--clear] [--force] | aktualisiert den WTHB-Inhaltsverzeichnis-Cache (Produktion) | ja |
test-text-tag-collector.php | eigenständige Unit-Tests für den Text-Tag-Collector | nein |
test-link-classifier.php | eigenständige Unit-Tests für den Link-Klassifizierer (inkl. Ziel-Extraktion) | nein |
test-uid-tag-collector.php | eigenständige Unit-Tests für den UID-Tag-Collector | nein |
test-uid-index-service.php | eigenständige Unit-Tests für den UID-Index-Service | nein |
tests/smoke-text-tag-collector.php [limit] | scannt Datensätze mit erweiterten Links, gibt jeden erfassten Wert aus | ja |
smoke-migration5.php | SQLite-Smoke-Test für Migration5 (UID-Index-Schema) | ja |
tests/p1-measure.php [--tree=<id>] | Read-only-Scaling-Messung für die XREF-Übersicht | ja |
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
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:
- Issues → Auf Codeberg öffnen
- Pull Requests → Auf Codeberg öffnen
- Übersetzungen → Auf Codeberg Weblate beitragen
Mirror-Repository: https://github.com/bschwede/linkenhancer
3 - Projektstruktur
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
- PHP (
composer.json):bschwede/wt-shared-libs(Path-Repo../wt-shared-libs)phpstan(dev).- Konflikte mit:
webtrees <2.2undelysch/webtrees-mitalteli-misc-features
- JS (
package.json):- Laufzeit-Abhängigkeit
tiny-markdown-editor - Dev-Tooling — Gulp, Rollup, PostCSS (autoprefixer, cssnano), Mocha + chai + sinon, jsdom
- Laufzeit-Abhängigkeit
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.
| Spalte | Typ | Hinweise |
|---|---|---|
id | int, PK (auto) | |
path | varchar(255) | Routen-Pfad |
handler | varchar(150) | vollqualifizierte Handler-Klasse |
handler_key | varchar(150), index | versionsneutrale Key (2.2.6 + 2.3 kompatibel) |
method | varchar(20) | |
extras | varchar(60) | |
subcontext | varchar(250) | |
category | varchar(60) | standard / custom module / generic … |
order | int, Default 10 | |
url | varchar(250) | |
updated_at | timestamp, nullable |
Index auf (path, handler) und auf handler_key.
le_record_scan
Eine Zeile pro gescanntem GEDCOM-Datensatz, mit einem MD5-Fingerprint des Datensatztextes — lässt dem CLI-Index eine inkrementelle Diff-Prüfung zu.
| Spalte | Typ | Hinweise |
|---|---|---|
file | int | Baum-ID (Teil des PK) |
xref | varchar(20) | (Teil des PK) |
rectype | varchar(15) | GEDCOM-Tag, z.B. INDI, OBJE (Teil des PK) |
hash | varchar(32) | MD5 des Datensatztextes |
scanned_at | timestamp, nullable |
Primärschlüssel (file, xref, rectype).
le_link_index
Eine Zeile pro (Link, Ziel) in einem Datensatz — das Fundament für die geplante Backlink-Funktion.
| Spalte | Typ | Hinweise |
|---|---|---|
id | int, PK (auto) | |
file | int | Baum-ID |
xref | varchar(20) | |
rectype | varchar(15) | |
tag_path | varchar(255) | wo der Link im Datensatz sitzt |
link_class | varchar(10) | xref / ext / pic / classic / other |
token | text | das rohe Link-Token |
snippet | text, nullable | zum Build-Zeitpunkt erfasster Anzeigekontext |
target_xref | varchar(20), nullable | XREF-Link-Ziel |
target_tree | varchar(255), nullable | |
target_uid | varchar(255), nullable, index | UID-Link-Ziel (per Längen-Heuristik befüllt) |
Indizes auf (file, xref, rectype), target_xref und target_uid.
le_uid_index
Eine Zeile pro in einem Datensatz gefundenem UID-Tag (Datensatz- und Fakt-Ebene). Kein Unique-Constraint — UIDs sind nicht eindeutig garantiert.
| Spalte | Typ | Hinweise |
|---|---|---|
id | int, PK (auto) | |
file | int | Baum-ID |
xref | varchar(20) | |
uid | varchar(255), index | UID-Wert wörtlich (ohne Normalisierung) |
rectype | varchar(15) | |
tag_path | varchar(255) | |
hash | varchar(32) | MD5-Fingerprint (inkrementelle Änderungsprüfung) |
Indizes auf uid und (file, xref, rectype).
le_index_meta
Eine einzelne Zeile (id = 1) mit dem Frische-Zustand von beiden — dem Link-Index und dem UID-Index.
| Spalte | Typ | Hinweise |
|---|---|---|
id | int, PK, Default 1 | einzelne Zeile |
last_run | timestamp, nullable | letzter vollständiger Link-Index-Lauf |
rows | int, Default 0 | Zeilenzahl des Link-Index |
uid_last_run | timestamp, nullable | letzter vollständiger UID-Index-Lauf |
uid_rows | int, Default 0 | Zeilenzahl des UID-Index |
4 - Klassenhierarchie
Automatisch generiert durch docs/tools/generate-class-hierarchy.php — nicht manuell bearbeiten.
Modul
LinkEnhancerModuleextendsAbstractModuleimplementsMiddlewareInterface,ModuleCustomInterface,ModuleGlobalInterface,ModuleConfigInterface,ModuleDataFixInterface,ModuleListInterface,ModuleTabInterface,SettingInterface—LinkEnhancerModule.phpSettingInterface—SettingInterface.phpLinkEnhancerUtils—LinkEnhancerUtils.phpLinkEnhancerModuleExtextendsLinkEnhancerModuleimplementsModuleExtGlobalInterface—LinkEnhancerModuleExt.php
CommonMark (Extensions)
LeImageRenderer(final) implementsNodeRendererInterface,XmlNodeRendererInterface,ConfigurationAwareInterface—CommonMark/LeImageRenderer.phpSmartPunctHelper—CommonMark/SmartPunctHelper.phpLeTableOfContentsRenderer(final) implementsNodeRendererInterface,XmlNodeRendererInterface—CommonMark/LeTableOfContentsRenderer.php
Factories
CustomMarkdownFactoryextendsMarkdownFactory—Factories/CustomMarkdownFactory.php
Request-Handler
DataFixBlocksActionimplementsRequestHandlerInterface—Http/RequestHandlers/DataFixBlocksAction.phpHelpWthbActionimplementsRequestHandlerInterface—Http/RequestHandlers/HelpWthbAction.phpXrefDetailData(final) implementsRequestHandlerInterface—Http/RequestHandlers/XrefDetailData.phpXrefOverviewListData(final) implementsRequestHandlerInterface—Http/RequestHandlers/XrefOverviewListData.phpGotoIdTreeActionextendsGotoIdAction—Http/RequestHandlers/GotoIdTreeAction.phpGotoUidTreeActionextendsGotoUidAction—Http/RequestHandlers/GotoUidTreeAction.phpGotoXrefActionimplementsRequestHandlerInterface—Http/RequestHandlers/GotoXrefAction.phpDataFixRebuildIndexActionimplementsRequestHandlerInterface—Http/RequestHandlers/DataFixRebuildIndexAction.phpRenumberActionHandler(final) —Http/RequestHandlers/RenumberActionHandler.phpHelpMdActionimplementsRequestHandlerInterface—Http/RequestHandlers/HelpMdAction.phpAdminXrefOverviewData(final) implementsRequestHandlerInterface—Http/RequestHandlers/AdminXrefOverviewData.phpGotoIdActionextendsAbstractGotoAction—Http/RequestHandlers/GotoIdAction.phpHelpWtCoreActionimplementsRequestHandlerInterface—Http/RequestHandlers/HelpWtCoreAction.phpWthbAdminHandler(final) —Http/RequestHandlers/WthbAdminHandler.phpGotoUidActionextendsAbstractGotoAction—Http/RequestHandlers/GotoUidAction.phpAbstractGotoAction(abstract) implementsRequestHandlerInterface—Http/RequestHandlers/AbstractGotoAction.php
Schema
SeedHelpTableimplementsSeedInterface—Schema/SeedHelpTable.phpMigration6implementsMigrationInterface—Schema/Migration6.phpMigration2implementsMigrationInterface—Schema/Migration2.phpMigration7implementsMigrationInterface—Schema/Migration7.phpMigration3implementsMigrationInterface—Schema/Migration3.phpMigration4implementsMigrationInterface—Schema/Migration4.phpMigration1implementsMigrationInterface—Schema/Migration1.phpMigration5implementsMigrationInterface—Schema/Migration5.phpMigration0implementsMigrationInterface—Schema/Migration0.php
Services
AdminSettingsBuilder(final) —Services/AdminSettingsBuilder.phpMarkdownEditorActivationService(final) —Services/MarkdownEditorActivationService.phpIndexRebuildScheduler(final) —Services/IndexRebuildScheduler.phpUidTagCollector(final) —Services/UidTagCollector.phpTextTagCollector(final) —Services/TextTagCollector.phpContentBuilder(final) —Services/ContentBuilder.phpRenumberWithLinksService(final) —Services/RenumberWithLinksService.phpDataFiles(final) —Services/DataFiles.phpXrefOverviewColumns(final) —Services/XrefOverviewColumns.phpLinkIndexService(final) —Services/LinkIndexService.phpXrefsService(final) —Services/XrefsService.phpXrefDetailService(final) —Services/XrefDetailService.phpWthbService—Services/WthbService.phpIdResolver(final) —Services/IdResolver.phpLinkRenderer(final) —Services/LinkRenderer.phpWthbTocService(final) —Services/WthbTocService.phpUidIndexService(final) —Services/UidIndexService.php
DataFix
FixHandlerInterface—DataFix/FixHandlerInterface.phpDataFixDispatcher—DataFix/DataFixDispatcher.phpTreeLookupTrait—DataFix/TreeLookupTrait.phpDanglingLinksFix(final) implementsFixHandlerInterface—DataFix/Handlers/DanglingLinksFix.phpDuplicateUidFix(final) implementsFixHandlerInterface—DataFix/Handlers/DuplicateUidFix.phpWtTypeFix(final) implementsFixHandlerInterface—DataFix/Handlers/WtTypeFix.phpXrefUidSwapFix(final) implementsFixHandlerInterface—DataFix/Handlers/XrefUidSwapFix.phpAmbiguousUidFix(final) implementsFixHandlerInterface—DataFix/Handlers/AmbiguousUidFix.php
5 - Entwicklungsumgebung
Was man zum Bauen und Testen des Moduls lokal braucht.
Voraussetzungen
| Komponente | Version | Hinweise |
|---|---|---|
| PHP | 8.3+ | dieselbe Version, mit der die webtrees-Instanz läuft |
| Composer | 2.x | installiert wt-shared-libs + phpstan |
| Node.js | 18+ (LTS) | für Gulp / Rollup / Mocha |
| webtrees | 2.2.x oder 2.3.x | nötig für PHPStan (app/-Klassen) und die Smoke-Tests |
| wt-shared-libs | neueste Version | Schwester-Repo unter modules_v4/wt-shared-libs (Composer-Path-Repo) |
Lage
Das Modul liegt innerhalb einer webtrees-Installation unter modules_v4/linkenhancer/. Sein composer.json
zieht die Schwester-Bibliothek als lokales Path-Repository ein:
"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.jsonsetztprepend-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
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
| Task | Befehl | Wirkung |
|---|---|---|
build | npx gulp build | clean → JS (Rollup) + CSS (PostCSS) |
devbuild | npx gulp devbuild | wie build, aber Dev-Mode (kein Minify, SourceMaps) |
updatepo | npx gulp updatepo | führt update-po-files.sh
aus → xgettext über src/ → messages.pot |
po2php | npx gulp po2php | kompiliert resources/lang/*.po → resources/lang/*.php |
syncversion | npx gulp syncversion | hält latest-version.txt
↔ LinkEnhancerModule.php
synchron |
bumpversion | npx gulp bumpversion | erfordert einen sauberen git-Tree → Version-Bump → syncversion |
archive | npx gulp archive | build + po2php + create-archive.sh
(Release-ZIP) |
forgejo:issue-refs | npx 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*undresources/css/bundle*. - JS (Rollup): zwei Entry-Bundles —
jsIndex(das Haupt-index-le.js) undjsExtractLeConfig(le-config.js). Ausgabe nachresources/js/bundle*. - CSS (PostCSS): Dateibasierte Subtasks aus
getCssSubTasks(), mit autoprefixer + cssnano. Ausgabe nachresources/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
updateporegeneriert nur die Vorlage (messages.pot). Die.po-Dateien selbst werden über Weblate aktualisiert, nicht lokal, um Konflikte zu vermeiden — siehe die Seite Übersetzungs-Pipeline.po2phpwandelt die committeten.po-Dateien in die PHP-Arrays um, die webtrees zur Laufzeit lädt.
7 - Übersetzungs-Pipeline
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.translateist 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-Updatemessages.potmit dem Core-POT vergleichen und neu abgedeckte msgids aufxlate()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
Ü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
- GenWiki Webtrees Handbuch: https://wiki.genealogy.net/Webtrees_Handbuch
- Code-Repositories:
- Weblate-Übersetzungskomponente: https://translate.codeberg.org/projects/wt-modules/linkenhancer-wthb-toc
- GenWiki Bot-Projekt: https://gitlab.genealogy.net/genwiki/wikibots (gw-skripte / pywikibot Dokumentation)
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:
- GenWiki-Daten abfragen (pywikibot READ)
- TOC generieren
pagefromfile-Wrapper-Datei bauen- Template zurück nach GenWiki schreiben (pywikibot WRITE)
- gerendertes Template per Parse-Check prüfen (curl + jq)
- JSON in das externe Repo deployen
- nachgelagert: Repo-CI, Weblate und Modul holen die Daten
GenWiki-Datenabfrage (pywikibot READ)
Schritt 1 ist wt-maintain.py, ein pywikibot-Bot (SectionsExportBot, erbt von ExistingPageBot). Er ruft site.login() auf, iteriert allpages(prefix="Webtrees Handbuch") und fragt pro Seite die MediaWiki-API (action=parse&prop=sections) plus imagelinks() (FilePage-Info) ab.
Ergebnis sind drei CSV-Dateien:
wthb-pageinfo.csv,wthb-sections.csvundwthb-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-Quellewthb-toc.json— die strukturierten Daten, die das Modul verbrauchtde.json— die deutsche Sprachdatei für Weblatewthb-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.shkopiertwthb-toc.jsonundde.jsonin das Repolinkenhancer-wthb-toc-lang, führttools/gen_meta.pyaus (regeneriertlanguages.json+version.json) und machtgit add/commit/push. Es ist standalone aufrufbar und skippt weich (Warnung +exit 0), wenn Repo oder JSON fehlen, sodass es denset -e-Orchestrator nie abbricht.- Repo-CI (
regen-meta.yml) führtgen_toc_html.pyaus 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:
WthbTocService.phpholt die Sprach-JSON von Codeberg Pages und stellt sie über einen zwei-Schichten-Cache bereit.refresh-wthb-toc.php(undcli/validate-wthb-toc.php) aktualisiert bzw. prüft diesen Cache, typischerweise per Cron.help-wthb-toc.phtmlrendert das TOC im Hilfe-Dialog.
Konfiguration & Voraussetzungen
setenv.sh— exportiertPWB_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:Webtreesmüssen von Hand gepflegt werden. pagefromfile-Login — laut Code-Kommentar muss ggf.site.login()inpagefromfile.pyeingefügt werden.- Soft-Skip im Deploy — fehlendes Repo oder JSON ist eine Warnung, kein Fehler (hält
set -esicher).
9 - Testing
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
| Test | Abgedeckt |
|---|---|
test-text-tag-collector.php | Text-Tag-Collector |
test-link-classifier.php | Link-Klassifizierer (inkl. Ziel-Extraktion) |
test-uid-tag-collector.php | UID-Tag-Collector |
test-uid-index-service.php | UID-Index-Service |
test-i18n-mark.php | i18n-Marker |
test-wthb-toc-service.php | WTHB-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
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.phpregistriert die PSR-4-Namespaces direkt (umgeht den versionsgeprüften Autoloader inwt-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-AssertionsignoreErrorsfür:AccessLevelEnum (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 — dercomposer-Script-Wrapper leitet zusätzliche Argumente nicht weiter.
11 - Release
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:
- Liest die Version aus
latest-version.txt. rsyncdas Modul nachdist/linkenhancer/und schließt alles aus, das nicht ausgeliefert wird (unten).- Erzeugt
dist/linkenhancer_v<version>.zip.
Aus dem Archiv ausgeschlossen
create-archive.sh lässt unter anderem aus: node_modules/, .git*, gulpfile.mjs, package*.json, phpstan*, tests/, die util/*-Buildskripte, resources/js/src, resources/js/tests, resources/img/, resources/lang/*.po / *.mo und .env. Ausgeliefert werden nur die gebauten bundle*-JS/CSS, die kompilierten resources/lang/*.php, src/, resources/views/, resources/css/ und die Modul-Entry-Dateien.
Veröffentlichen
- Das Release in git taggen.
dist/linkenhancer_v<version>.zipmit 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.