Entwickler-orientierte Dokumentation: Architektur, Dateistruktur, DCAT-AP-Feldmapping, REST-API, Erweiterbarkeit, WP-CLI und Abhängigkeiten. Zurück zur Übersicht: README · Vertiefende Spezifikation & Roadmap: TECHNICAL-SPEC.md.
Präsentation → WP-Admin-Masken, Wizard (Tabs, Validierung)
Domäne → Metadatenmodell, DCAT-AP Mapping, Validierungslogik
Infrastruktur → REST API, JSON-LD Serialisierung, Custom Post Type
open-data-wizard/
├── open-data-wizard.php # Plugin-Header & Bootstrap
├── uninstall.php # Opt-in Datenlöschung
├── composer.json
├── config/
│ ├── licenses.txt # Lizenzdatei (URI | Label)
│ ├── dct-format-list.php # Dateiformate (MIME + EU-URI)
│ ├── dcat-ap-fields.php # Felddefinitionen (Qualität + Validierung)
│ ├── field-catalog.php # Feld-Katalog (Quelle der Feld-Referenz)
│ ├── shacl/ # Offizielle SHACL-Shapes (EU + DCAT-AP.de) + Anleitung
│ ├── TopicClassification-4.2.3_de-4.2.3.rdf # CESSDA SKOS/RDF (95 Konzepte)
│ ├── phpcs.xml # PHPCS Konfiguration
│ ├── phpstan.neon # PHPStan Level 6
│ └── phpunit.xml # PHPUnit 9 Konfiguration
├── vendor/ # Carbon Fields + Dev-Dependencies
├── includes/
│ ├── class-post-types.php # CPT-Registrierung: odw_dataset
│ ├── class-fields.php # Carbon Fields (5 Tabs), JSON-LD Builder
│ ├── class-rest-api.php # REST Endpoints + Transient-Cache
│ ├── class-validation.php # Pflichtfeldprüfung vor Veröffentlichung
│ ├── class-quality.php # Qualitätsindikatoren (0–100, 4 Stufen)
│ ├── class-admin.php # Listenansicht, wp.media Meta-Box, Help Tabs
│ ├── class-shortcode.php # [odw_dataset]-Shortcode, Download-Card
│ ├── class-catalog.php # [odw_catalog]-Shortcode + Block, Datenkatalog
│ ├── class-catalog-filters.php # Katalog: Filter, Facetten, Suche, Index-Felder
│ ├── class-catalog-seo.php # Katalog: 404, Canonical, Titel, robots, Cache-Action
│ ├── class-setup.php # Aktivierung, Willkommenshinweis, Migrationen
│ ├── class-samples.php # Beispieldaten laden/entfernen, nie im Endpunkt
│ ├── class-batch-import.php # CSV/JSON-Import
│ ├── class-settings.php # Einstellungsseite
│ └── class-cli.php # WP-CLI Befehle
├── assets/
│ ├── js/
│ │ ├── wizard-tabs.js # Tab-Navigation (Vanilla JS)
│ │ ├── odw-file-upload.js # wp.media Upload-Widget (jQuery)
│ │ └── odw-admin-fields.js # License/CESSDA Auto-Suggest, Dateigröße-Widget
│ └── css/
│ ├── admin.css # Admin-Styles
│ └── frontend.css # Download-Card + Datenkatalog
├── tests/
│ ├── bootstrap.php
│ ├── test-fields.php
│ ├── test-fields-extended.php
│ ├── test-quality.php
│ ├── test-settings.php
│ ├── test-shortcode.php
│ ├── test-catalog.php
│ ├── test-catalog-filters.php
│ ├── test-catalog-seo.php
│ ├── test-rest-delta.php
│ ├── test-batch-import.php
│ ├── test-samples.php
│ ├── test-validation.php, test-rdf.php, test-vocabulary.php, …
│ ├── test-cli.php
│ ├── shacl/ # SHACL-Fixtures + validate.mjs
│ └── e2e/ # Playwright-Specs + mu-plugins/odw-e2e-seed.php
└── .github/workflows/ci.yml # CI: PHPCS + PHPStan + PHPUnit (PHP 8.1–8.3)
📋 Vollständige, mehrstufige Feld-Referenz: docs/FELD-REFERENZ.md dokumentiert jedes Formularfeld in vier Stufen (DCAT-AP-Frage · verständliche Frage · DCAT-AP-Langbeschreibung · verständliche Langbeschreibung). Sie wird aus
config/field-catalog.phpgeneriert (php bin/generate-field-reference.phpbzw.wp open-data-wizard docs).
| Feld | DCAT-AP Prädikat | Pflicht |
|---|---|---|
| Titel | dct:title |
✓ |
| Beschreibung | dct:description |
✓ |
| Herausgeber | dct:publisher → foaf:Organization |
✓ |
| Thema | dcat:theme |
— |
| Feld | DCAT-AP Prädikat | Pflicht |
|---|---|---|
| Sprache | dct:language |
— |
| Schlagworte (eine pro Zeile) | dcat:keyword |
— |
| Veröffentlichungsdatum | dct:issued |
— |
| Änderungsdatum | dct:modified (auto) |
— |
| CESSDA Themenklassifikation | cessda:topic |
— |
| Feld | DCAT-AP Prädikat | Pflicht |
|---|---|---|
| Zugriffs-URL | dcat:accessURL |
✓ (min. 1) |
| Format | dct:format (MIME) |
— |
| Dateigröße | dcat:byteSize |
— |
| Lizenz | dct:license (URI) |
✓ pro Distribution |
| Zuschreibungstext | dcatde:licenseAttributionByText |
— |
| Planbare Verfügbarkeit | dcatap:availability (@id, DCAT-AP.de-URI) |
— |
| Feld | DCAT-AP Prädikat | Pflicht |
|---|---|---|
| Projektseite | dcat:landingPage (@id) |
— |
| Aktualisierungsfrequenz | dct:accrualPeriodicity (EU-URI) |
— |
| Geographische Abdeckung | dct:spatial → dct:Location + skos:prefLabel |
— |
| Zeitlicher Bezug Start | dct:temporal → dcat:startDate |
— |
| Zeitlicher Bezug Ende | dct:temporal → dcat:endDate |
— |
| Kontaktpunkt Name | dcat:contactPoint → vcard:fn |
— |
| Kontaktpunkt E-Mail | vcard:hasEmail (mit mailto:-Prefix) |
— |
| Kontaktpunkt Website | vcard:hasURL (@id) |
— |
| HVD-Markierung | (steuert HVD-Ausgabe) | — |
| HVD-Kategorie | dcatap:hvdCategory (@id, EU-URI) |
✓ wenn HVD |
| HVD-Rechtsgrundlage (auto) | dcatap:applicableLegislation (Reg. 2023/138) |
— |
| Bereitstellende Stelle | dcatde:contributorID (@id, GovData-Verzeichnis) |
— |
| Urheber (Name/E-Mail) | dcatde:originator → foaf:Agent |
— |
| Pflegende Stelle (Name/E-Mail) | dcatde:maintainer → foaf:Agent |
— |
| Feld | Interner Meta-Key | Beschreibung |
|---|---|---|
| Attachment-ID | _odw_file_id |
Mediathek-Datei |
| Dateigröße (auto) | _odw_file_size |
Bytes, auto-berechnet beim Speichern |
| Dateiformat (auto) | _odw_file_format |
z.B. „CSV", auto-berechnet |
GET /wp-json/datenatlas/v1/catalog
| Parameter | Standard | Beschreibung |
|---|---|---|
page |
1 | Seitennummer |
per_page |
20 | Einträge pro Seite (max. 100) |
theme |
– | Filter nach Thema |
license |
– | Filter: Kurzform (cc-by) oder volle URI |
format |
jsonld |
jsonld → application/ld+json, json → application/json |
Response-Header: X-WP-Total, X-WP-TotalPages, X-ODW-Cache (HIT/MISS)
GET /wp-json/datenatlas/v1/datasets/<id>
GET /wp-json/datenatlas/v1/delta?since=2024-01-01T00:00:00Z
| Parameter | Standard | Beschreibung |
|---|---|---|
since |
erforderlich | ISO 8601 Datetime |
page |
1 | Seitennummer |
per_page |
20 | Einträge pro Seite (max. 100) |
format |
jsonld |
Content-Type |
Response enthält dcat:dataset (geänderte Datasets) und odw:removed (Tombstones).
Wie im Datenkatalog der Website: /catalog und /delta lassen sie weg, /datasets/<id>
antwortet mit 404. Wird ein Datensatz nachträglich geschützt, erscheint er in /delta unter
odw:removed.
Datensätze mit dem Meta-Schlüssel _odw_sample (geladen über „Beispieldaten laden" bzw.
wp open-data-wizard samples load, siehe ODW_Samples) liefert keiner der drei Endpunkte aus:
/catalog und /delta schließen sie per meta_query (NOT EXISTS) aus, auch bei den
Löschmeldungen; /datasets/<id> antwortet mit derselben 404 wie für eine unbekannte ID. Im
Frontend-Katalog sieht sie nur, wer manage_open_data hat (oder ein Katalog mit
show_demo="1"), samt Trefferzahlen und Jahresliste der Filter. Die Kennzeichnung entfällt nur
über „Als eigenen Datensatz übernehmen" (ODW_Samples::adopt()), nicht durch Bearbeiten.
{
"@context": {
"dcat": "https://www.w3.org/ns/dcat#",
"dct": "http://purl.org/dc/terms/",
"foaf": "http://xmlns.com/foaf/0.1/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"vcard": "http://www.w3.org/2006/vcard/ns#",
"skos": "http://www.w3.org/2004/02/skos/core#",
"odw": "https://github.com/daimpad/OpenDataWizard/ns#"
},
"@type": "dcat:Catalog",
"dct:title": "Mein Datenkatalog",
"dcat:dataset": [
{
"@type": "dcat:Dataset",
"dct:title": "Mitgliederdaten 2023",
"dct:description": "Anonymisierte Mitgliederstatistik.",
"dct:publisher": { "@type": "foaf:Organization", "foaf:name": "Musterorganisation e.V." },
"dcat:distribution": [
{
"@type": "dcat:Distribution",
"dcat:accessURL": "https://organisation.de/daten/mitglieder.csv",
"dct:format": "text/csv",
"dct:license": "https://creativecommons.org/licenses/by/4.0/",
"dcat:byteSize": 20480
}
]
}
]
}| Hook | Beschreibung |
|---|---|
odw_license_options |
Weitere Lizenz-Optionen hinzufügen |
odw_theme_options |
Weitere Thema-Optionen hinzufügen |
odw_dataset_jsonld |
JSON-LD Array vor Ausgabe anpassen |
odw_catalog_title |
Catalog-Titel anpassen |
odw_catalog_query_args |
WP_Query-Argumente der Frontend-Katalogliste anpassen ($args, $config, $request, seit 2.44.0 $state mit Filtern/Suche) |
odw_license_is_open |
Zählt eine Lizenz als Open Data (Katalogfilter „Nur Open Data")? ($open, $uri) — Standard: Abschnitt „Freie Nutzung" in config/licenses.txt; wirkt beim nächsten Speichern bzw. Index-Abgleich |
odw_catalog_changed (Action) |
Ein Datensatz, der im öffentlichen Katalog steht oder stand, wurde gespeichert, veröffentlicht, zurückgezogen oder gelöscht ($post_id) — zum Leeren von Seiten-Caches |
// Eigene Lizenz hinzufügen
add_filter( 'odw_license_options', function( array $options ): array {
$options['https://example.com/custom-license'] = 'Custom License 1.0';
return $options;
} );
// Katalogseite aus dem Seiten-Cache werfen, sobald sich ein Datensatz ändert
// (Beispiel WP Rocket; die ID der Katalogseite ist hier 42)
add_action( 'odw_catalog_changed', function( int $post_id ): void {
if ( function_exists( 'rocket_clean_post' ) ) {
rocket_clean_post( 42 );
}
} );
// JSON-LD Dataset anpassen
add_filter( 'odw_dataset_jsonld', function( array $jsonld, int $post_id ): array {
$jsonld['dct:spatial'] = 'https://sws.geonames.org/2921044/';
return $jsonld;
}, 10, 2 );# Qualitätsscores für alle veröffentlichten Datasets neu berechnen
wp open-data-wizard quality recalculate
# Alle Datasets einschließlich Entwürfe und Trash berechnen
wp open-data-wizard quality recalculate --all
# Alle REST API Transient-Caches löschen
wp open-data-wizard cache clear
# Beispieldaten laden, Stand anzeigen, endgültig löschen
wp open-data-wizard samples load
wp open-data-wizard samples status
wp open-data-wizard samples remove --yes| Paket | Version | Zweck |
|---|---|---|
| Carbon Fields | ^3.6 | Admin-Formular (5-Tab-Wizard) |
| PHPUnit | ^9.6 | Unit-Tests (dev) |
| WP_Mock | ^1.0 | WordPress-Stubs für Tests (dev) |
| PHPStan + WordPress-Stubs | ^2.0 | Statische Analyse Level 6 (dev) |
| WPCS | ^3.1 | Coding Standards (dev) |
Dazu npm-Pakete für die Testumgebung (wp-env, Playwright, SHACL-Prüfung). Sie landen nicht im
Plugin-ZIP. Sicherheitslücken in ihren transitiven Abhängigkeiten werden über overrides in
package.json behoben (derzeit qs, body-parser, glob/minimatch); npm audit --package-lock-only soll 0 Funde melden.
.github/workflows/ci.yml läuft mit permissions: contents: read und prüft PHPCS, PHPStan,
PHPUnit (PHP 8.1–8.3), SHACL, das Release-Paket, die Übersetzungen und End-to-End (Playwright gegen
wp-env auf Deutsch). Die E2E-Specs stehen einzeln im Workflow — eine neue Spec-Datei braucht einen
eigenen Schritt.
Ein installationsfertiges Plugin-ZIP (nur Laufzeit-Dateien inkl. Produktions-vendor/ mit Carbon
Fields, ohne Tests/Doku/Dev-Configs) erzeugt:
bin/build-release.sh
# → dist/open-data-wizard-<version>.zip (Version aus dem Plugin-Header)Installation in WordPress: Plugins → Installieren → Plugin hochladen. Das Skript nutzt
composer install --no-dev, entschlackt die Carbon-Fields-Dev-Dateien (die kompilierten
build/-Assets bleiben erhalten) und legt das ZIP unter dist/ ab (gitignored).
Automatische Releases: .github/workflows/release.yml baut dieses ZIP bei jedem Push auf main
und veröffentlicht es als GitHub-Release, sobald die Plugin-Version (ODW_VERSION) neu ist — für
dieselbe Version wird kein doppelter Release erzeugt. Es genügt also, die Version zu bumpen und zu
mergen; das ZIP erscheint dann unter Releases
(auch manuell via „Run workflow" auslösbar).