Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: ["**"]
pull_request:
branches: ["main", "master"]

jobs:
phpcs:
name: WordPress Coding Standards
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
tools: composer:v2

- name: Install dev dependencies
run: composer install --no-interaction --prefer-dist --dev

- name: Run PHPCS
run: composer phpcs

phpstan:
name: PHPStan Static Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
tools: composer:v2

- name: Install dependencies (prod + dev)
run: composer install --no-interaction --prefer-dist

- name: Run PHPStan
run: composer phpstan

phpunit:
name: PHPUnit Tests
runs-on: ubuntu-latest
strategy:
matrix:
php: ["8.1", "8.2", "8.3"]
steps:
- uses: actions/checkout@v4

- name: Set up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2

- name: Install dev dependencies
run: composer install --no-interaction --prefer-dist --dev

- name: Run PHPUnit
run: composer test
86 changes: 86 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Changelog

Alle nennenswerten Änderungen an diesem Projekt sind in dieser Datei dokumentiert.

Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
Versionierung folgt [Semantic Versioning](https://semver.org/).

---

## [1.3.0] — 2026-04-21

### Hinzugefügt
- **Qualitätsindikatoren / Ampellogik** (`includes/class-quality.php`): Automatische Bewertung der Metadaten-Vollständigkeit (0–100 Punkte, 3 Levels: Gut/Mittel/Verbesserungsbedarf)
- 10 Indikatoren in 3 Gruppen: Pflichtfelder (55 Pkt.), Empfohlene Felder (40 Pkt.), Optionale Angaben (5 Pkt.)
- Automatische Neuberechnung nach jedem Speichern (`save_post_odw_dataset`, Priorität 30)
- Persistenz in 4 Meta-Keys: `_odw_quality_score`, `_odw_quality_level`, `_odw_quality_indicators`, `_odw_quality_calculated_at`
- **Qualitätsspalte in der Admin-Listenansicht**: Farbiger Badge (● 85) mit Tooltip; sortierbar
- **Qualitätsbericht-Meta-Box** auf dem Edit-Screen: Fortschrittsbalken, Ampel-Badge, gruppierte Indikator-Tabelle (✓/✗) mit Punkten, Zeitstempel der letzten Berechnung
- **`odw:qualityScore` im JSON-LD**: Qualitätsdaten werden via `odw_dataset_jsonld` Filter an den REST-API Output angehängt (`odw:score`, `odw:maxScore`, `odw:level`, `odw:calculatedAt`)
- **`odw:` JSON-LD Namespace** (`https://github.com/daimpad/OpenDataWizard/ns#`) in `JSONLD_CONTEXT`
- **CSS Qualitäts-Styles**: `--odw-color-quality-*` Custom Properties; `.odw-quality-badge`, `.odw-quality-gauge`, `.odw-quality-table` Komponenten

---

## [1.2.0] — 2026-04-21

### Hinzugefügt
- **?format= Parameter** an beiden REST-Endpoints (`/catalog`, `/datasets/<id>`): `jsonld` (Standard, `application/ld+json`) oder `json` (`application/json`) — Grundlage für spätere Content-Negotiation
- **PHPStan Level 6** Konfiguration (`phpstan.neon`)
- **WordPress Coding Standards** via WPCS (`phpcs`/`phpcbf` Scripts in composer.json)
- **PHPUnit** Test-Setup (`phpunit.xml`, `tests/bootstrap.php`, erste Test-Suite für `ODW_Fields`)
- **GitHub Actions CI** Workflow (`.github/workflows/ci.yml`): PHPCS, PHPStan, PHPUnit auf PHP 8.1/8.2/8.3
- **`ODW_Fields::get_required_fields()`** — zentrale Pflichtfeld-Registry als Single Source of Truth

### Geändert
- **Validierungslogik zentralisiert**: `class-validation.php` iteriert über `ODW_Fields::get_required_fields()` statt Felder doppelt zu pflegen
- **`get_field_value()`** vereinfacht: CF-Key-Parameter entfernt, meta_key reicht als Identifier
- **composer.json**: `require-dev` Sektion mit PHPStan, WPCS, PHPUnit hinzugefügt; `allow-plugins` Konfiguration ergänzt

---

## [1.1.0] — 2026-04-21

### Hinzugefügt
- **Activation Hook**: CPT registrieren, Rewrite Rules flushen, Capability `manage_open_data` vergeben
- **Deactivation Hook**: Rewrite Rules flushen
- **`uninstall.php`**: Opt-in Datenlöschung bei Deinstallation (hinter `odw_delete_data_on_uninstall` Option)
- **REST API Transient-Cache**: 5 Minuten TTL für `/catalog` und `/datasets/<id>`; Cache-Invalidierung bei `save_post_odw_dataset` und `trashed_post`; `X-ODW-Cache: HIT/MISS` Header
- **Capability `manage_open_data`**: Administrator und Editor erhalten die Capability bei Plugin-Aktivierung
- **Filter-Hooks**: `odw_license_options`, `odw_theme_options`, `odw_dataset_jsonld`, `odw_catalog_title`
- **Admin Help Tabs**: DCAT-AP Feldbeschreibungen und Harvest-Endpoint Doku auf dem Edit-Screen
- **`ODW_Fields::get_license_label()`**: Single Source of Truth für Lizenz-URI → Label Übersetzung
- **CSS Custom Properties**: `--odw-color-*` Variablen statt hard-codierter Hex-Werte

### Behoben
- **Zeitzonen-Bug**: `gmdate()` → `current_time()` für `_odw_modified` (verhinderte Datums-Abweichung um 1 Tag bei Nicht-UTC-Servern)
- **Sortierbare Spalte „Thema"**: `pre_get_posts` Hook mit `meta_key`/`meta_value` — Sortierung war vorher defekt
- **`$_GET` Sanitization**: `wp_unslash()` + `sanitize_text_field()` konsequent; `absint()` für post_id (class-admin.php, class-validation.php)
- **Byte-Size Validierung**: `is_numeric()` + `>= 0` Prüfung vor JSON-LD Ausgabe
- **Transient-TTL**: 60s → 300s für Validierungsnotices (verhindert Ablauf bei langsamen Servern)
- **sessionStorage Safety**: `try/catch` Wrapper für Private-Browsing-Modus und Quota-Überschreitung; post_id-spezifischer Key (`odw_active_tab_<id>`)
- **MutationObserver Speicherleck**: `disconnect()` via `beforeunload` Event
- **Carbon Fields Boot-Fehler**: `try/catch` um `boot()` mit hilfreicher Admin-Notice statt fatalen PHP-Fehler

---

## [1.0.0] — 2026-03-02

### Hinzugefügt
- **Custom Post Type `odw_dataset`** mit deutschen Labels und Dashicons-database Icon
- **Carbon Fields Formular** mit 4 Tabs:
- Tab 1: Pflichtfelder (Titel, Beschreibung, Publisher, Lizenz)
- Tab 2: Optionale Felder (Sprache, Schlagworte, Thema, Datum)
- Tab 3: Distributionen (accessURL, Format, byteSize) — wiederholbares Complex Field
- Tab 4: JSON-LD Vorschau (read-only)
- **REST API**:
- `GET /wp-json/datenatlas/v1/catalog` mit Paginierung und Filtern (`?theme=`, `?license=`)
- `GET /wp-json/datenatlas/v1/datasets/<id>`
- Content-Type `application/ld+json`, DCAT-AP 3.0 `@context`
- **Admin-Listenansicht**: Spalten Titel, Lizenz, Thema, Status, Änderungsdatum; Status-Dropdown-Filter
- **Pflichtfeldvalidierung**: Blockiert Veröffentlichung bei fehlenden Pflichtfeldern; Admin-Notice mit Feldnamen
- **Tab-Navigation** (Vanilla JS, kein jQuery): sessionStorage-Persistenz, Keyboard-Navigation
- **Carbon Fields** v3.6 via Composer im Plugin gebündelt (kein Composer-Wissen nötig)
- **DCAT-AP 3.0 JSON-LD** Ausgabe mit allen Pflicht- und empfohlenen Feldern
- **Lizenz-Kurzaliase** im API-Filter (`?license=cc-by`, `?license=cc0` etc.)
- Automatische Aktualisierung von `dct:modified` bei jedem Speichern
90 changes: 71 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ Das Plugin stellt einen öffentlichen REST API Endpoint bereit:

```
GET https://deine-website.de/wp-json/datenatlas/v1/catalog
GET https://deine-website.de/wp-json/datenatlas/v1/datasets/<id>
```

Diese URL kann bei einer Open-Data-Plattform als Harvest-Quelle eingetragen werden — einmalig, ohne weiteren Aufwand.

Parameter: `page`, `per_page`, `theme`, `license`, `format` (`jsonld` oder `json`)

### ✅ DCAT-AP 3.0 Konformität
Alle Ausgaben sind DCAT-AP 3.0 konform und in JSON-LD serialisiert.

Expand All @@ -90,16 +93,27 @@ Keine weiteren Abhängigkeiten. Keine Programmierkenntnisse erforderlich.
```bash
git clone https://github.com/daimpad/OpenDataWizard.git
cd OpenDataWizard
composer install
composer install # inkl. PHPStan, WPCS, PHPUnit
```

Den Plugin-Ordner in eine lokale WordPress-Instanz einbinden (z.B. via [LocalWP](https://localwp.com)).

**Systemvoraussetzungen:**
- WordPress ≥ aktuelle LTS-Version
- WordPress ≥ 6.4
- PHP ≥ 8.1
- Composer (nur für Entwicklung)

**Dev-Tools:**

```bash
composer phpcs # WordPress Coding Standards prüfen
composer phpcbf # Automatisch korrigieren
composer phpstan # Statische Analyse (Level 6)
composer test # PHPUnit-Tests ausführen
```

CI läuft via GitHub Actions (`.github/workflows/ci.yml`) auf PHP 8.1, 8.2 und 8.3.

---

## Technische Dokumentation
Expand All @@ -117,17 +131,24 @@ 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 bei Deinstallation
├── composer.json
├── phpstan.neon
├── phpunit.xml
├── vendor/ # Carbon Fields (gebündelt)
├── includes/
│ ├── class-post-types.php # CPT-Registrierung: odw_dataset
│ ├── class-fields.php # Carbon Fields + DCAT-AP Mapping
│ ├── class-rest-api.php # REST Endpoints
│ ├── class-fields.php # Carbon Fields, DCAT-AP Mapping, Pflichtfeld-Registry
│ ├── class-rest-api.php # REST Endpoints mit Transient-Cache
│ ├── class-validation.php # Pflichtfeldprüfung
│ └── class-admin.php # Listenansicht & Admin-Notices
│ └── class-admin.php # Listenansicht, Help Tabs, Assets
├── assets/
│ ├── js/wizard-tabs.js
│ └── css/admin.css
│ ├── js/wizard-tabs.js # Tab-Navigation (Vanilla JS)
│ └── css/admin.css # Admin-Styles (CSS Custom Properties)
├── tests/
│ ├── bootstrap.php
│ └── test-fields.php
├── .github/workflows/ci.yml
└── languages/
```

Expand All @@ -154,7 +175,17 @@ open-data-wizard/
```
GET /wp-json/datenatlas/v1/catalog
```
Liefert alle veröffentlichten Datasets als `dcat:Catalog` in JSON-LD. Parameter: `page`, `per_page`, `theme`, `license`.
Liefert alle veröffentlichten Datasets als `dcat:Catalog` in JSON-LD.

| Parameter | Standard | Beschreibung |
|------------|----------|----------------------------------------------------|
| `page` | 1 | Seitennummer |
| `per_page` | 20 | Einträge pro Seite (max. 100) |
| `theme` | – | Filter nach Thema (z.B. `Bildung`) |
| `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`)

#### Einzel-Dataset
```
Expand Down Expand Up @@ -195,25 +226,34 @@ GET /wp-json/datenatlas/v1/datasets/<id>

### Erweiterbarkeit

Das Plugin stellt Hooks für eigene Felder und Profile bereit:
Das Plugin stellt folgende WordPress-Filter zur Erweiterung bereit:

```php
// Eigene Felder hinzufügen
add_filter('odw_extra_fields', function($fields) {
return $fields;
});
| 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 |

// JSON-LD Output anpassen
add_filter('odw_jsonld_dataset', function($jsonld, $post_id) {
```php
// 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;
} );

// JSON-LD Dataset anpassen
add_filter( 'odw_dataset_jsonld', function( array $jsonld, int $post_id ): array {
$jsonld['dct:spatial'] = 'https://sws.geonames.org/2921044/'; // Deutschland
return $jsonld;
}, 10, 2);
}, 10, 2 );
```

### Abhängigkeiten

| Paket | Version | Lizenz |
|---|---|---|
| [Carbon Fields](https://carbonfields.net/) | ^3.0 | MIT |
| [Carbon Fields](https://carbonfields.net/) | ^3.6 | MIT |

---

Expand Down Expand Up @@ -243,6 +283,18 @@ git push origin feature/mein-feature

---

## Deinstallation

Das Plugin löscht bei Deinstallation standardmäßig **keine** Daten (Opt-in). Um alle Plugin-Daten zu löschen, die Option `odw_delete_data_on_uninstall` auf `true` setzen:

```php
update_option( 'odw_delete_data_on_uninstall', true );
```

Danach das Plugin im WordPress-Backend deinstallieren.

---

## Lizenz

MIT License — siehe [`LICENSE`](./LICENSE)
GPL-2.0-or-later — siehe [`LICENSE`](./LICENSE)
Loading
Loading