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
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,62 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.ht

---

## [2.35.8] — 2026-08-07

Datei-Upload steht dort, wo er hingehört.

### 🔧 Changed
- **Der Datei-Upload ist keine Seitenleisten-Box mehr,** sondern steht in Tab 3 unmittelbar unter
der Zugriffs-URL — der Angabe, zu der er die Alternative ist. Vorher waren es zwei getrennte
Orte für denselben Sachverhalt: Link im Formular, Datei in der Seitenleiste. Das lud zu
widersprüchlichen Angaben ein, ohne dass die Oberfläche den Zusammenhang zeigte. Die Hilfetexte
beider Felder verweisen jetzt aufeinander.

### ℹ️ Technische Hinweise
- Das Widget ist ein Carbon-Fields-`html`-Feld, dessen Inhalt über einen **Callback** entsteht.
`Html_Field::to_json()` ruft ihn erst beim Rendern des Containers auf, dort steht der
Beitragskontext zur Verfügung — der aktuelle Dateizustand wird also weiterhin serverseitig
ausgegeben und hängt nicht davon ab, dass JavaScript ihn nachträgt.
- `odw-file-upload.js` nutzt jetzt durchgehend **delegierte Handler** an `document` statt direkter
Bindung beim Laden. Carbon Fields hängt seine Felder erst nach `DOMContentLoaded` ein und kann
sie später neu rendern; direkt gebundene Handler hätten ins Leere gegriffen.
- `save_file_attachment()` ist unverändert und bricht weiterhin ab, wenn die Nonce fehlt — sollte
das Widget einmal nicht rendern, geht eine bestehende Verknüpfung nicht verloren.

---

## [2.35.7] — 2026-08-06

Zweite Runde aus dem Usability-Test: Gliederung und Verständlichkeit.

### 🔧 Changed
- **Tab-Zuschnitt nach inhaltlicher Zusammengehörigkeit.** Schlagworte stehen jetzt in Tab 1 bei
Thema, CESSDA-Klassifikation und Engagementfeld — alle vier beschreiben, worum es geht, und
gehörten nie zu Sprache und Datumsangaben. Die Erstveröffentlichung ist nach Tab 4 zur
Aktualisierungsfrequenz gewandert. Tab 2 heißt entsprechend **„Sprache & Übersetzungen"** und
trägt jetzt sämtliche Übersetzungs-Repeater (Titel, Beschreibung, Schlagworte), die vorher auf
zwei Tabs verteilt waren.
- **Zugriffsrechte sind mit „Öffentlich" vorbelegt.** Für ein Open-Data-Plugin ist das der
Normalfall; die Vorbelegung steht sichtbar im Formular und lässt sich jederzeit ändern. Bewusst
kein Automatismus aus dem Beitragsstatus — Metadaten sollen nicht im Hintergrund entstehen.
- **Die HVD-Felder sagen jetzt, für wen sie gedacht sind.** High-Value-Datensätze sind eine
Rechtskategorie der EU-Durchführungsverordnung 2023/138 für öffentliche Stellen. Beschriftung und
Hilfetext nennen das ausdrücklich, damit Vereine und Verbände die Felder überspringen können,
ohne zu rätseln. Für Kommunen bleiben sie unverändert nutzbar.

### 🐛 Fixed
- **Das CESSDA-Eingabefeld war viel zu schmal** und schnitt seinen Platzhalter ab. Das Widget wird
als Geschwister neben das ausgeblendete Carbon-Fields-Feld gesetzt und steht damit außerhalb von
`.cf-field` — dessen Breitenregeln griffen nicht, und der Browser fiel auf die Standardbreite
eines Textfelds zurück (`size=20`, rund 250 px).

### 🌍 i18n
- 11 geänderte Zeichenketten in `.pot`/`.po`, `.mo` neu kompiliert (628 Einträge, 0 unübersetzt).
Ein neues Prüfskript vergleicht alle übersetzbaren Zeichenketten des Quellcodes gegen den
Katalog — damit fällt eine vergessene Übersetzung künftig sofort auf.

---

## [2.35.6] — 2026-08-06

Automatische Updates laufen wieder.
Expand Down
40 changes: 20 additions & 20 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Externe Harvester rufen /catalog, /datasets/<id>, oder /delta ab
| **ODW_Fields** | Carbon Fields Formular (5 Tabs) + JSON-LD Builder | `register_required_fields()`, `odw_build_dataset_jsonld()` |
| **ODW_Validation** | Publish-Blocking bei fehlenden Pflichtfeldern | `intercept_publish()`, `validate()` |
| **ODW_Quality** | Qualitäts-Scoring & Caching | `calculate()`, `get_level()` |
| **ODW_Admin** | Admin UI (Spalten, Intro-Seite, File-Upload Meta-Box) | `register_introduction_page()`, `render_column()`, `save_file_attachment()` |
| **ODW_Admin** | Admin UI (Spalten, Intro-Seite, Datei-Upload-Widget in Tab 3) | `register_introduction_page()`, `render_column()`, `file_upload_html()`, `save_file_attachment()` |
| **ODW_Rest_API** | REST Endpoints mit Transient-Caching | `get_catalog()`, `get_dataset()`, `get_delta()` |
| **ODW_Rdf** | JSON-LD → Turtle (dependency-frei) für RDF-Harvester | `to_turtle()` |
| **ODW_Field_Reference** | Erzeugt `docs/FELD-REFERENZ.md` aus `config/field-catalog.php` | `build()`, `write()`, `js_map()` |
Expand Down Expand Up @@ -213,30 +213,30 @@ Grund: Hohe Sichtbarkeit, mehrere Klassen verwenden sie, kein `self::`-Overhead.
```php
- odw_publisher (REQUIRED)
→ Herausgebende Organisation (dct:publisher)

- odw_theme
→ In welche Kategorie gehört dieser Datensatz? (dcat:theme)


- odw_description (REQUIRED)
→ Worum geht es in diesem Datensatz? (dct:description)

- odw_theme
→ In welche Kategorie gehört dieser Datensatz? (dcat:theme)

- odw_keywords
→ Mit welchen Schlagworten finde ich diese Daten? (dcat:keyword)

// Aufklappbar „Weitere Einordnung (optional)":
- odw_cessda_topic → CESSDA Topic Classification (dct:subject)
- odw_engagementfeld → ZiviZ-Engagementfeld des Datensatzes (dct:subject)
```

### Tab 2: Inhaltliche Angaben
### Tab 2: Sprache & Übersetzungen
```php
- odw_language
→ In welcher Sprache sind die Daten? (dct:language)

- odw_keywords
→ Mit welchen Stichworten finde ich diese Daten? (dcat:keyword)

- odw_issued
→ Veröffentlichungsdatum (dct:issued)

- odw_modified (Auto-Update!)
→ Änderungsdatum (dct:modified) — wird bei jedem Save aktualisiert

- odw_cessda_topic
→ CESSDA Topic Classification (Auto-Suggest aus CDV 4.2.3)

// Übersetzungs-Repeater (mehrsprachige Literale):
- odw_title_translations
- odw_description_translations
- odw_keyword_translations
```

### Tab 3: Datenbereitstellung (SIMPLIFIED v2.1.4!)
Expand Down Expand Up @@ -722,7 +722,7 @@ Update **both** locations:
- MINOR: New features (backward-compatible)
- PATCH: Bug fixes only

Current: **v2.35.6**
Current: **v2.35.8**

---

Expand Down Expand Up @@ -861,6 +861,6 @@ Neue technische Festlegungen gehören dorthin, nicht in README oder CLAUDE.md.

---

**Zuletzt aktualisiert**: Version 2.35.6 (August 2026)
**Zuletzt aktualisiert**: Version 2.35.8 (August 2026)
**Autor**: Open Data Wizard Team (nozilla)
**License**: GPL-2.0-or-later
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
![PHP Version](https://img.shields.io/badge/PHP-%3E%3D%208.1-8892BF?style=flat-square&logo=php&logoColor=white)
![WordPress](https://img.shields.io/badge/WordPress-compatible-21759B?style=flat-square&logo=wordpress&logoColor=white)
![DCAT-AP](https://img.shields.io/badge/DCAT--AP-3.0-brightgreen?style=flat-square)
![Version](https://img.shields.io/badge/Version-2.35.6-brightgreen?style=flat-square)
![Version](https://img.shields.io/badge/Version-2.35.8-brightgreen?style=flat-square)
![PRs Welcome](https://img.shields.io/badge/PRs-willkommen-brightgreen?style=flat-square)

📖 [Dokumentation](DOCUMENTATION.md) · 📋 [Feld-Referenz](docs/FELD-REFERENZ.md) · 📐 [Technische Spezifikation](TECHNICAL-SPEC.md) · 📝 [Changelog](CHANGELOG.md) · 🛡️ [Security](SECURITY.md) · ⚖️ [Lizenz](LICENSE)
Expand Down Expand Up @@ -76,10 +76,10 @@ Das Wizard-Formular wurde vollständig überarbeitet, um es auch ohne DCAT-AP-Ke
### 🧭 Geführter Wizard
Fünf-Tab-Assistent mit praktischen Beispielen. Pflichtfelder sind mit einem roten Sternchen (`*`) gekennzeichnet; als **Entwurf** lässt sich jederzeit unvollständig speichern — erst zum **Veröffentlichen** müssen alle Pflichtangaben ausgefüllt sein.

1. **Grundlegende Informationen** — „Wer gibt diese Daten heraus?", „Worum geht es in diesem Datensatz?", „Welchem Thema ist dieser Datensatz zugeordnet?". Weniger häufige Angaben (CESSDA-Themenklassifikation, ZiviZ-Engagementfeld, Titel-/Beschreibungs-Übersetzungen) liegen in einer aufklappbaren Untergruppe am Tab-Ende.
2. **Inhaltliche Angaben** — „In welcher Sprache sind die Daten?", „Mit welchen Schlagworten finde ich diese Daten?", Veröffentlichungs- und Änderungsdatum
1. **Grundlegende Informationen** — „Wer gibt diese Daten heraus?", „Worum geht es in diesem Datensatz?", „Welchem Thema ist dieser Datensatz zugeordnet?", „Mit welchen Schlagworten finde ich diese Daten?". Weniger häufige Einordnungen (CESSDA-Themenklassifikation, ZiviZ-Engagementfeld) liegen in einer aufklappbaren Untergruppe am Tab-Ende.
2. **Sprache & Übersetzungen** — die Sprache der Daten sowie Titel, Beschreibung und Schlagworte in weiteren Sprachen
3. **Datenbereitstellung** — Zugriffs-URL **oder** Datei-Upload (Mediathek), Format, Dateigröße, **Lizenz (Pflicht je Distribution)**, Namensnennungstext; optional weitere Distributionen (wiederholbar)
4. **Erweiterte Angaben** — Projektseite, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen, Verantwortlichkeiten (Urheber/pflegende Stelle, GovData-Contributor-ID), High-Value-Datensatz (HVD) Kategorie
4. **Erweiterte Angaben** — Projektseite, Erstveröffentlichung, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen, Verantwortlichkeiten (Urheber/pflegende Stelle, GovData-Contributor-ID), High-Value-Datensatz (HVD) Kategorie
5. **Vorschau** — generiertes JSON-LD live einsehen

### 🏷 Lizenz-Auswahl
Expand Down Expand Up @@ -147,7 +147,7 @@ Die Batch-Import-Seite ist vollständig ins WordPress-Admin-Design integriert (S
[📥 CSV-Beispiel herunterladen](./samples/import-example.csv) | [📄 JSON-Beispiel](./samples/import-example.json)

### 📎 Datei-Upload (Mediathek)
Sidebar-Meta-Box — vollständig unabhängig von Carbon Fields:
Steht in Tab 3 direkt unter der Zugriffs-URL, zu der er die Alternative ist:
- „Datei auswählen / hochladen"-Button öffnet den nativen WordPress Media Library Frame
- Beim Speichern werden `_odw_file_size` (Bytes) und `_odw_file_format` (z.B. „CSV") automatisch berechnet
- Sicherheit: `wp_verify_nonce` + `current_user_can('edit_post')`
Expand Down
11 changes: 10 additions & 1 deletion assets/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,17 @@
font-size: 13px;
}

.odw-cessda-input {
/* Das Widget wird als Geschwister neben das ausgeblendete .cf-field gesetzt,
liegt also außerhalb des Carbon-Fields-Kontexts. Dessen Breitenregeln für
Eingabefelder greifen hier deshalb nicht, und der Browser fiel auf die
Standardbreite (size=20, ~250 px) zurück — das Feld war sichtbar kürzer als
alle anderen und schnitt seinen Platzhalter ab. Die Selektorkette schlägt
die Admin-Regeln von WordPress und Carbon Fields sicher. */
input.odw-cessda-input,
.odw-cessda-widget .odw-cessda-input {
box-sizing: border-box;
width: 100%;
max-width: 100%;
}

.odw-cessda-hint {
Expand Down
123 changes: 61 additions & 62 deletions assets/js/odw-file-upload.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,78 @@
/* global wp, odwFileUpload */
/**
* Native WordPress Media Library integration for the Download-Datei meta box.
* Native WordPress Media Library integration for the download-file widget.
*
* Das Widget steht als Carbon-Fields-html-Feld in Tab 3 (früher: eigene
* Meta-Box in der Seitenleiste). Carbon Fields hängt seine Felder erst nach
* DOMContentLoaded ein und kann sie später neu rendern — Handler, die beim
* Laden direkt an die Elemente gebunden werden, gingen dabei ins Leere.
* Deshalb durchgehend delegierte Handler an `document` und Elementzugriffe
* erst im Moment des Klicks.
*
* Der Zustand (ausgewählte Datei) wird serverseitig gerendert, siehe
* ODW_Admin::file_upload_html() — ohne JavaScript bleibt eine bestehende
* Verknüpfung also erhalten und geht beim Speichern nicht verloren.
*
* Depends on: jquery, wp.media (loaded via wp_enqueue_media())
*/
( function ( $ ) {
'use strict';

var frame;

var $preview = $( '#odw-file-preview' );
var $fileName = $( '#odw-file-name' );
var $input = $( '#odw-file-id-input' );
var $selectBtn = $( '#odw-file-select-btn' );
var $removeBtn = $( '#odw-file-remove-btn' );
'use strict';

// Restore UI state from server-rendered data on page load.
if ( odwFileUpload.currentId > 0 && odwFileUpload.currentName ) {
_setHasFile( odwFileUpload.currentName );
}
var frame;

// Open the WordPress Media Library frame.
$selectBtn.on( 'click', function ( e ) {
e.preventDefault();
/**
* Setzt die UI in den „Datei ausgewählt"-Zustand.
*
* @param {string} name Dateiname oder Attachment-Titel aus der Mediathek.
*/
function setHasFile( name ) {
$( '#odw-file-name' ).text( name );
$( '#odw-file-preview' )
.removeClass( 'odw-file-preview--empty' )
.addClass( 'odw-file-preview--has-file' );
$( '#odw-file-remove-btn' ).prop( 'disabled', false );
}

if ( frame ) {
frame.open();
return;
}
/**
* Setzt die UI in den leeren Zustand (keine Datei ausgewählt).
*/
function setEmpty() {
$( '#odw-file-name' ).text( odwFileUpload.labels.noFile );
$( '#odw-file-preview' )
.removeClass( 'odw-file-preview--has-file' )
.addClass( 'odw-file-preview--empty' );
$( '#odw-file-remove-btn' ).prop( 'disabled', true );
}

frame = wp.media( {
title: odwFileUpload.labels.frameTitle,
button: { text: odwFileUpload.labels.frameButton },
multiple: false,
} );
// Mediathek öffnen.
$( document ).on( 'click', '#odw-file-select-btn', function ( e ) {
e.preventDefault();

frame.on( 'select', function () {
var attachment = frame.state().get( 'selection' ).first().toJSON();
$input.val( attachment.id );
_setHasFile( attachment.filename || attachment.title );
} );
if ( frame ) {
frame.open();
return;
}

frame.open();
} );
frame = wp.media( {
title: odwFileUpload.labels.frameTitle,
button: { text: odwFileUpload.labels.frameButton },
multiple: false,
} );

// Clear the current attachment selection.
$removeBtn.on( 'click', function ( e ) {
e.preventDefault();
$input.val( 0 );
_setEmpty();
} );
frame.on( 'select', function () {
var attachment = frame.state().get( 'selection' ).first().toJSON();
$( '#odw-file-id-input' ).val( attachment.id );
setHasFile( attachment.filename || attachment.title );
} );

/**
* Setzt die UI in den „Datei ausgewählt"-Zustand.
*
* @param {string} name Dateiname oder Attachment-Titel aus der Mediathek.
*/
function _setHasFile( name ) {
$fileName.text( name );
$preview
.removeClass( 'odw-file-preview--empty' )
.addClass( 'odw-file-preview--has-file' );
$removeBtn.prop( 'disabled', false );
}
frame.open();
} );

/**
* Setzt die UI in den leeren Zustand (keine Datei ausgewählt).
* Wird beim Klick auf „Entfernen" aufgerufen.
*/
function _setEmpty() {
$fileName.text( odwFileUpload.labels.noFile );
$preview
.removeClass( 'odw-file-preview--has-file' )
.addClass( 'odw-file-preview--empty' );
$removeBtn.prop( 'disabled', true );
}
// Verknüpfung entfernen.
$( document ).on( 'click', '#odw-file-remove-btn', function ( e ) {
e.preventDefault();
$( '#odw-file-id-input' ).val( 0 );
setEmpty();
} );

} )( jQuery );
Loading
Loading