From 717eeaf42d3de7f32ebfec58cdfabbd70b403e8f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 11:27:13 +0000 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20Zweite=20Runde=20Usability-Test=20?= =?UTF-8?q?=E2=80=94=20Gliederung=20und=20Verst=C3=A4ndlichkeit=20(v2.35.7?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tab-Zuschnitt nach inhaltlicher Zusammengehörigkeit: - Schlagworte stehen jetzt in Tab 1 bei Thema, CESSDA-Klassifikation und Engagementfeld. Alle vier beschreiben, worum es geht; bei Sprache und Datumsangaben waren sie fehl am Platz. Sie bleiben oberhalb der aufklappbaren Untergruppe sichtbar — an ihnen hängen 30 MQA-Punkte. - Die Erstveröffentlichung ist nach Tab 4 zur Aktualisierungsfrequenz gewandert; beide beschreiben die Aktualität des Datensatzes. - Tab 2 heißt "Sprache & Übersetzungen" und trägt sämtliche Übersetzungs-Repeater, die vorher auf zwei Tabs verteilt waren. Zugriffsrechte mit "Öffentlich" vorbelegt — sichtbar im Formular und änderbar. Bewusst kein Automatismus aus dem Beitragsstatus: Metadaten sollen nicht im Hintergrund entstehen. HVD-Felder nennen ihre Geltung. High-Value-Datensätze sind eine Rechtskategorie der EU-Durchführungsverordnung 2023/138 für öffentliche Stellen; Beschriftung und Hilfetext sagen das jetzt, damit Vereine und Verbände sie überspringen können. Für Kommunen unverändert nutzbar. CESSDA-Eingabefeld war viel zu schmal und schnitt seinen Platzhalter ab. Das Widget steht als Geschwister neben dem ausgeblendeten .cf-field, also außerhalb des Carbon-Fields-Kontexts — dessen Breitenregeln griffen nicht und der Browser fiel auf size=20 zurück (~250 px). Mitgezogen: Feld-Katalog (Tab-Zuordnung, Reihenfolge, korrigierte Engagementfeld-Frage), Validierungs-Tabname, Einstiegsseite, E2E-Spec, README, CLAUDE.md, docs/FELD-REFERENZ.md neu generiert. i18n: 11 geänderte Zeichenketten, .mo neu kompiliert (628 Einträge). Neu bin/check-i18n.py — vergleicht alle übersetzbaren Zeichenketten des Quellcodes gegen den Katalog und meldet Lücken mit Exit-Code 1. Verifiziert ohne lokalen Composer-Toolchain (Proxy blockt weiterhin api.github.com): php -l über alle geänderten Dateien, node --check, Katalog-Abdeckungslogik nachgestellt (beide Richtungen leer), Feld-Referenz deckungsgleich mit dem Generator, check-i18n.py bei 0. https://claude.ai/code/session_01JB1xUQM892bVZ4Yv3MZjvq --- CHANGELOG.md | 32 +++++++ CLAUDE.md | 38 ++++----- README.md | 8 +- assets/css/admin.css | 11 ++- bin/check-i18n.py | 95 +++++++++++++++++++++ config/field-catalog.php | 88 +++++++++---------- docs/FELD-REFERENZ.md | 118 +++++++++++++------------- includes/class-admin.php | 8 +- includes/class-fields.php | 127 +++++++++++++++++----------- includes/class-validation.php | 2 +- languages/open-data-wizard-en_US.mo | Bin 65725 -> 66465 bytes languages/open-data-wizard-en_US.po | 44 +++++----- languages/open-data-wizard.pot | 20 ++--- open-data-wizard.php | 4 +- tests/e2e/admin-workflow.spec.js | 4 +- 15 files changed, 380 insertions(+), 219 deletions(-) create mode 100755 bin/check-i18n.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 226383c0..f379957e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,38 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.ht --- +## [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. diff --git a/CLAUDE.md b/CLAUDE.md index a787a707..873a914f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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!) @@ -722,7 +722,7 @@ Update **both** locations: - MINOR: New features (backward-compatible) - PATCH: Bug fixes only -Current: **v2.35.6** +Current: **v2.35.7** --- @@ -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.7 (August 2026) **Autor**: Open Data Wizard Team (nozilla) **License**: GPL-2.0-or-later diff --git a/README.md b/README.md index 49f227f7..38cb90e3 100644 --- a/README.md +++ b/README.md @@ -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.7-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) @@ -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 diff --git a/assets/css/admin.css b/assets/css/admin.css index 364c708d..c1e5c43c 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -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 { diff --git a/bin/check-i18n.py b/bin/check-i18n.py new file mode 100755 index 00000000..c419d02c --- /dev/null +++ b/bin/check-i18n.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Prüft, ob alle übersetzbaren Zeichenketten im Katalog stehen. + +Extrahiert jede Zeichenkette, die im PHP-Quellcode durch eine der +i18n-Funktionen mit der Textdomain `open-data-wizard` läuft, und vergleicht sie +gegen die msgid-Einträge von `languages/open-data-wizard-en_US.po`. + +Hintergrund: Die Quellsprache des Plugins ist Deutsch, die englische Fassung +überschreibt sie auf en_US-Installationen. Eine beim Umformulieren vergessene +Übersetzung fällt dort sonst erst auf, wenn mitten im englischen Backend ein +deutscher Satz steht. + +Aufruf aus dem Projektverzeichnis: + + python3 bin/check-i18n.py + +Exit-Code 0, wenn nichts fehlt, sonst 1 samt Liste der fehlenden Einträge. +""" + +import glob +import io +import os +import re +import sys + +FUNCS = r"(?:__|_e|esc_html__|esc_html_e|esc_attr__|esc_attr_e)" +# Einfache Anführungszeichen: kein Escaping von \n nötig, PHP nimmt sie wörtlich. +PAT_SQ = re.compile(FUNCS + r"\(\s*'((?:[^'\\]|\\.)*)'\s*,\s*'open-data-wizard'\s*\)") +# Doppelte Anführungszeichen: PHP interpretiert \n und \t. +PAT_DQ = re.compile(FUNCS + r'\(\s*"((?:[^"\\]|\\.)*)"\s*,\s*\'open-data-wizard\'\s*\)') + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def source_strings(): + """Alle übersetzbaren Zeichenketten aus dem Plugin-Quellcode.""" + found = set() + files = [os.path.join(ROOT, 'open-data-wizard.php')] + files += sorted(glob.glob(os.path.join(ROOT, 'includes', '*.php'))) + + for path in files: + src = io.open(path, encoding='utf-8').read() + for match in PAT_SQ.finditer(src): + found.add(match.group(1).replace("\\'", "'").replace('\\\\', '\\')) + for match in PAT_DQ.finditer(src): + value = match.group(1) + value = value.replace('\\n', '\n').replace('\\t', '\t').replace('\\"', '"') + found.add(value) + + found.discard('') + return found + + +def catalog_strings(po_path): + """Alle msgid-Einträge der Übersetzungsdatei.""" + text = io.open(po_path, encoding='utf-8').read() + entries = set() + for match in re.finditer(r'^msgid "((?:[^"\\]|\\.)*)"$', text, re.M): + value = match.group(1) + value = value.replace('\\"', '"').replace('\\n', '\n') + value = value.replace('\\t', '\t').replace('\\\\', '\\') + entries.add(value) + return entries + + +def main(): + po_path = os.path.join(ROOT, 'languages', 'open-data-wizard-en_US.po') + if not os.path.isfile(po_path): + sys.stderr.write('Übersetzungsdatei nicht gefunden: %s\n' % po_path) + return 1 + + source = source_strings() + catalog = catalog_strings(po_path) + missing = sorted(s for s in source if s not in catalog) + + print('Quelle: %d Zeichenketten, Katalog: %d Einträge' % (len(source), len(catalog))) + + if not missing: + print('OK — keine fehlenden Übersetzungen.') + return 0 + + print('Fehlend: %d' % len(missing)) + for value in missing: + preview = value.replace('\n', '\\n') + if len(preview) > 120: + preview = preview[:120] + '…' + print(' - ' + preview) + print('\nBitte in .pot und .po ergänzen, danach:') + print(' python3 bin/compile-mo.py languages/open-data-wizard-en_US.po ' + 'languages/open-data-wizard-en_US.mo') + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/config/field-catalog.php b/config/field-catalog.php index 0afb235d..2539b224 100644 --- a/config/field-catalog.php +++ b/config/field-catalog.php @@ -61,6 +61,18 @@ 'desc_dcat' => '`dcat:theme` ordnet den Datensatz einer oder mehreren Kategorien zu. In DCAT-AP wird das kontrollierte EU-Vokabular „Data Theme" (`http://publications.europa.eu/resource/authority/data-theme/`) verwendet, z. B. `ENVI` (Umwelt) oder `EDUC` (Bildung). Kardinalität 0..n.', 'desc_human' => 'Wählen Sie das Themengebiet, zu dem die Daten am besten passen — ähnlich einer Schublade, in die der Datensatz einsortiert wird. Das hilft anderen, Ihre Daten über Themenfilter zu finden. Beispiel: Umwelt, Bildung, Gesundheit, Wirtschaft, Kultur.', ), + array( + 'key' => 'keywords', + 'meta_key' => '_odw_keywords', + 'dcat_prop' => 'dcat:keyword', + 'tab' => '1 — Grundlegende Informationen', + 'tier' => 'recommended', + 'vocab' => '', + 'q_dcat' => 'Welche `dcat:keyword` (Schlagwörter) beschreiben den Datensatz?', + 'q_human' => 'Mit welchen Schlagworten finde ich diese Daten?', + 'desc_dcat' => '`dcat:keyword` sind freie, sprachlich getaggte Schlagwörter zur Verbesserung der Auffindbarkeit (`literal-lang`, Kardinalität 0..n). Anders als `dcat:theme` sind sie nicht an ein kontrolliertes Vokabular gebunden. Je Schlagwort ein Wert.', + 'desc_human' => 'Tragen Sie einzelne Schlagwörter ein, unter denen man Ihre Daten suchen würde — jedes Wort in eine eigene Zeile. Sie ergänzen das Thema und machen die Daten leichter auffindbar. Beispiel: Umwelt, Wasser, Luftverschmutzung.', + ), array( 'key' => 'cessda_topic', 'meta_key' => '_odw_cessda_topic', @@ -81,7 +93,7 @@ 'tier' => 'optional', 'vocab' => 'engagementfeld', 'q_dcat' => 'Welches `dct:subject` (Engagementfeld) nach ZiviZ-Vokabular beschreibt den Datensatz?', - 'q_human' => 'In welchem Engagementfeld ist die Organisation aktiv?', + 'q_human' => 'Welchem Engagementfeld ist dieser Datensatz zuzuordnen?', 'desc_dcat' => '`dct:subject` mit einem Konzept aus dem ZiviZ-Vokabular „Engagementfeld" (`https://ziviz.de/def/engagementfeld/`), das zivilgesellschaftliche Tätigkeitsfelder klassifiziert. Der Wert ist die Konzept-URI; die Label→URI-Auflösung erfolgt beim Speichern. Mehrfachangabe möglich (Kardinalität 0..n).', 'desc_human' => 'Optional für Organisationen der Zivilgesellschaft: Ordnen Sie den Datensatz einem Engagementfeld zu — also dem gesellschaftlichen Bereich, in dem Sie aktiv sind. Wählen Sie einen Eintrag aus der Liste; die passende Kennung wird automatisch gesetzt. Beispiel: Kultur, Sport, Umwelt- und Naturschutz.', ), @@ -99,14 +111,14 @@ ), // ========================================================================= - // Tab 2 — Inhaltliche Angaben + // Tab 2 — Sprache & Übersetzungen // ========================================================================= array( 'key' => 'language', 'meta_key' => '_odw_language', 'dcat_prop' => 'dct:language', - 'tab' => '2 — Inhaltliche Angaben', + 'tab' => '2 — Sprache & Übersetzungen', 'tier' => 'recommended', 'vocab' => 'language', 'q_dcat' => 'In welcher `dct:language` (Sprache) liegen die Daten vor?', @@ -114,47 +126,6 @@ 'desc_dcat' => '`dct:language` gibt die Sprache des Datensatzes an, referenziert über das EU-Authority-Vokabular „Language" (`http://publications.europa.eu/resource/authority/language/`, z. B. `DEU`, `ENG`). Kardinalität 0..n — mehrsprachige Datensätze können mehrere Sprachen angeben.', 'desc_human' => 'Geben Sie an, in welcher Sprache die Inhalte der Daten verfasst sind (z. B. die Spaltenüberschriften und Texte). Das hilft Nutzenden einzuschätzen, ob sie die Daten verstehen. Beispiel: Deutsch, Englisch.', ), - array( - 'key' => 'keywords', - 'meta_key' => '_odw_keywords', - 'dcat_prop' => 'dcat:keyword', - 'tab' => '2 — Inhaltliche Angaben', - 'tier' => 'recommended', - 'vocab' => '', - 'q_dcat' => 'Welche `dcat:keyword` (Schlagwörter) beschreiben den Datensatz?', - 'q_human' => 'Mit welchen Schlagworten finde ich diese Daten?', - 'desc_dcat' => '`dcat:keyword` sind freie, sprachlich getaggte Schlagwörter zur Verbesserung der Auffindbarkeit (`literal-lang`, Kardinalität 0..n). Anders als `dcat:theme` sind sie nicht an ein kontrolliertes Vokabular gebunden. Je Schlagwort ein Wert.', - 'desc_human' => 'Tragen Sie einzelne Schlagwörter ein, unter denen man Ihre Daten suchen würde — jedes Wort in eine eigene Zeile. Sie ergänzen das Thema und machen die Daten leichter auffindbar. Beispiel: Umwelt, Wasser, Luftverschmutzung.', - ), - array( - 'key' => 'issued', - 'meta_key' => '_odw_issued', - 'dcat_prop' => 'dct:issued', - 'tab' => '2 — Inhaltliche Angaben', - 'tier' => 'recommended', - 'vocab' => '', - 'q_dcat' => 'Wann wurde der Datensatz `dct:issued` (erstveröffentlicht)?', - 'q_human' => 'Wann wurden diese Daten zum ersten Mal veröffentlicht?', - 'desc_dcat' => '`dct:issued` ist das Datum der formalen Erstveröffentlichung des Datensatzes, typisiert als `xsd:date` bzw. `xsd:dateTime`. Kardinalität 0..1. Nicht zu verwechseln mit `dct:modified` (letzte Änderung) oder dem Erhebungszeitraum (`dct:temporal`).', - 'desc_human' => 'Geben Sie das Datum an, an dem die Daten erstmals veröffentlicht wurden. Das ist der „Geburtstag" des Datensatzes, nicht der Zeitraum, den die Daten abdecken. Beispiel: 2024-01-15.', - ), - array( - 'key' => 'modified', - 'meta_key' => '_odw_modified', - 'dcat_prop' => 'dct:modified', - 'tab' => '2 — Inhaltliche Angaben', - 'tier' => 'recommended', - 'vocab' => '', - // Kein Formularfeld: Der Wert wird beim Speichern automatisch gesetzt. - // Das Flag nimmt den Eintrag von der Formular-Abdeckungsprüfung aus, - // ohne die Eigenschaft aus der Feld-Referenz zu entfernen — sie steht - // weiterhin im veröffentlichten JSON-LD. - 'auto' => true, - 'q_dcat' => 'Wann wurde der Datensatz zuletzt `dct:modified` (geändert)?', - 'q_human' => 'Wann wurden diese Daten zuletzt aktualisiert?', - 'desc_dcat' => '`dct:modified` gibt das Datum der letzten inhaltlichen Änderung des Datensatzes an (`xsd:date`/`xsd:dateTime`, Kardinalität 0..1). Das Plugin setzt diesen Wert bei jeder Speicherung automatisch; ein Eingabefeld gibt es bewusst nicht, da eine manuelle Angabe beim nächsten Speichern überschrieben würde.', - 'desc_human' => 'Das Datum der letzten Aktualisierung. Es wird beim Speichern automatisch gesetzt, sodass Nutzende immer sehen, wie aktuell die Daten sind — Sie müssen dafür nichts eintragen. Beispiel: 2026-04-22.', - ), // ========================================================================= // Tab 3 — Datenbereitstellung @@ -261,6 +232,35 @@ 'desc_dcat' => '`dcat:landingPage` verweist auf eine menschenlesbare Webseite mit weiteren Informationen zum Datensatz (Range `foaf:Document`, Kardinalität 0..n). Anders als `dcat:accessURL` führt sie nicht direkt zu den Daten, sondern zu Kontext, Dokumentation oder Projektbeschreibung.', 'desc_human' => 'Verlinken Sie eine Webseite, auf der man mehr über die Daten oder das Projekt erfährt — etwa eine Projekt- oder Dokumentationsseite. Das ist nicht der direkte Download, sondern die „Über uns"-Seite zu den Daten. Beispiel: https://beispiel.de/projekt.', ), + array( + 'key' => 'issued', + 'meta_key' => '_odw_issued', + 'dcat_prop' => 'dct:issued', + 'tab' => '4 — Erweiterte Angaben', + 'tier' => 'recommended', + 'vocab' => '', + 'q_dcat' => 'Wann wurde der Datensatz `dct:issued` (erstveröffentlicht)?', + 'q_human' => 'Wann wurden diese Daten zum ersten Mal veröffentlicht?', + 'desc_dcat' => '`dct:issued` ist das Datum der formalen Erstveröffentlichung des Datensatzes, typisiert als `xsd:date` bzw. `xsd:dateTime`. Kardinalität 0..1. Nicht zu verwechseln mit `dct:modified` (letzte Änderung) oder dem Erhebungszeitraum (`dct:temporal`).', + 'desc_human' => 'Geben Sie das Datum an, an dem die Daten erstmals veröffentlicht wurden. Das ist der „Geburtstag" des Datensatzes, nicht der Zeitraum, den die Daten abdecken. Beispiel: 2024-01-15.', + ), + array( + 'key' => 'modified', + 'meta_key' => '_odw_modified', + 'dcat_prop' => 'dct:modified', + 'tab' => '4 — Erweiterte Angaben', + 'tier' => 'recommended', + 'vocab' => '', + // Kein Formularfeld: Der Wert wird beim Speichern automatisch gesetzt. + // Das Flag nimmt den Eintrag von der Formular-Abdeckungsprüfung aus, + // ohne die Eigenschaft aus der Feld-Referenz zu entfernen — sie steht + // weiterhin im veröffentlichten JSON-LD. + 'auto' => true, + 'q_dcat' => 'Wann wurde der Datensatz zuletzt `dct:modified` (geändert)?', + 'q_human' => 'Wann wurden diese Daten zuletzt aktualisiert?', + 'desc_dcat' => '`dct:modified` gibt das Datum der letzten inhaltlichen Änderung des Datensatzes an (`xsd:date`/`xsd:dateTime`, Kardinalität 0..1). Das Plugin setzt diesen Wert bei jeder Speicherung automatisch; ein Eingabefeld gibt es bewusst nicht, da eine manuelle Angabe beim nächsten Speichern überschrieben würde.', + 'desc_human' => 'Das Datum der letzten Aktualisierung. Es wird beim Speichern automatisch gesetzt, sodass Nutzende immer sehen, wie aktuell die Daten sind — Sie müssen dafür nichts eintragen. Beispiel: 2026-04-22.', + ), array( 'key' => 'accrual_periodicity', 'meta_key' => '_odw_accrual_periodicity', diff --git a/docs/FELD-REFERENZ.md b/docs/FELD-REFERENZ.md index 64b94a7e..a73ae47a 100644 --- a/docs/FELD-REFERENZ.md +++ b/docs/FELD-REFERENZ.md @@ -19,14 +19,12 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld - **1 — Grundlegende Informationen** - [Wer gibt diese Daten heraus?](#wer-gibt-diese-daten-heraus) - [Welchem Thema ist dieser Datensatz zugeordnet?](#welchem-thema-ist-dieser-datensatz-zugeordnet) + - [Mit welchen Schlagworten finde ich diese Daten?](#mit-welchen-schlagworten-finde-ich-diese-daten) - [Welchem Themenfeld nach CESSDA ordnen Sie den Datensatz zu?](#welchem-themenfeld-nach-cessda-ordnen-sie-den-datensatz-zu) - - [In welchem Engagementfeld ist die Organisation aktiv?](#in-welchem-engagementfeld-ist-die-organisation-aktiv) + - [Welchem Engagementfeld ist dieser Datensatz zuzuordnen?](#welchem-engagementfeld-ist-dieser-datensatz-zuzuordnen) - [Worum geht es in diesem Datensatz?](#worum-geht-es-in-diesem-datensatz) -- **2 — Inhaltliche Angaben** +- **2 — Sprache & Übersetzungen** - [In welcher Sprache sind die Daten?](#in-welcher-sprache-sind-die-daten) - - [Mit welchen Schlagworten finde ich diese Daten?](#mit-welchen-schlagworten-finde-ich-diese-daten) - - [Wann wurden diese Daten zum ersten Mal veröffentlicht?](#wann-wurden-diese-daten-zum-ersten-mal-veröffentlicht) - - [Wann wurden diese Daten zuletzt aktualisiert?](#wann-wurden-diese-daten-zuletzt-aktualisiert) - **3 — Datenbereitstellung** - [Wo kann man die Datei herunterladen oder ansehen?](#wo-kann-man-die-datei-herunterladen-oder-ansehen) - [In welchem Format ist die Datei?](#in-welchem-format-ist-die-datei) @@ -37,6 +35,8 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld - [Wie dauerhaft ist diese Datei verfügbar?](#wie-dauerhaft-ist-diese-datei-verfügbar) - **4 — Erweiterte Angaben** - [Wo finde ich mehr Informationen zu diesem Projekt?](#wo-finde-ich-mehr-informationen-zu-diesem-projekt) + - [Wann wurden diese Daten zum ersten Mal veröffentlicht?](#wann-wurden-diese-daten-zum-ersten-mal-veröffentlicht) + - [Wann wurden diese Daten zuletzt aktualisiert?](#wann-wurden-diese-daten-zuletzt-aktualisiert) - [Wie oft werden diese Daten aktualisiert?](#wie-oft-werden-diese-daten-aktualisiert) - [Auf welcher Verwaltungsebene wurden diese Daten erhoben?](#auf-welcher-verwaltungsebene-wurden-diese-daten-erhoben) - [Auf welches amtliche Gebiet beziehen sich die Daten?](#auf-welches-amtliche-gebiet-beziehen-sich-die-daten) @@ -112,6 +112,23 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld **4 · Verständliche Langbeschreibung:** Wählen Sie das Themengebiet, zu dem die Daten am besten passen — ähnlich einer Schublade, in die der Datensatz einsortiert wird. Das hilft anderen, Ihre Daten über Themenfilter zu finden. Beispiel: Umwelt, Bildung, Gesundheit, Wirtschaft, Kultur. +### Mit welchen Schlagworten finde ich diese Daten? + +| Eigenschaft | Wert | +|---|---| +| DCAT-Property | `dcat:keyword` | +| Meta-Key | `_odw_keywords` | +| Stufe | Empfohlen | +| Vokabular | — | + +**1 · DCAT-AP-Frage:** Welche `dcat:keyword` (Schlagwörter) beschreiben den Datensatz? + +**2 · Verständliche Frage:** Mit welchen Schlagworten finde ich diese Daten? + +**3 · DCAT-AP-Langbeschreibung:** `dcat:keyword` sind freie, sprachlich getaggte Schlagwörter zur Verbesserung der Auffindbarkeit (`literal-lang`, Kardinalität 0..n). Anders als `dcat:theme` sind sie nicht an ein kontrolliertes Vokabular gebunden. Je Schlagwort ein Wert. + +**4 · Verständliche Langbeschreibung:** Tragen Sie einzelne Schlagwörter ein, unter denen man Ihre Daten suchen würde — jedes Wort in eine eigene Zeile. Sie ergänzen das Thema und machen die Daten leichter auffindbar. Beispiel: Umwelt, Wasser, Luftverschmutzung. + ### Welchem Themenfeld nach CESSDA ordnen Sie den Datensatz zu? | Eigenschaft | Wert | @@ -129,7 +146,7 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld **4 · Verständliche Langbeschreibung:** Optional für sozial- und wirtschaftswissenschaftliche Daten: Ordnen Sie den Datensatz einem Fachthema aus dem CESSDA-Katalog zu (ein europäischer Standard für Forschungsdaten). Tippen Sie das Thema ein und wählen Sie aus der Vorschlagsliste. Beispiel: Volkszählungen, Migration, Wirtschaftspolitik. -### In welchem Engagementfeld ist die Organisation aktiv? +### Welchem Engagementfeld ist dieser Datensatz zuzuordnen? | Eigenschaft | Wert | |---|---| @@ -140,7 +157,7 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld **1 · DCAT-AP-Frage:** Welches `dct:subject` (Engagementfeld) nach ZiviZ-Vokabular beschreibt den Datensatz? -**2 · Verständliche Frage:** In welchem Engagementfeld ist die Organisation aktiv? +**2 · Verständliche Frage:** Welchem Engagementfeld ist dieser Datensatz zuzuordnen? **3 · DCAT-AP-Langbeschreibung:** `dct:subject` mit einem Konzept aus dem ZiviZ-Vokabular „Engagementfeld" (`https://ziviz.de/def/engagementfeld/`), das zivilgesellschaftliche Tätigkeitsfelder klassifiziert. Der Wert ist die Konzept-URI; die Label→URI-Auflösung erfolgt beim Speichern. Mehrfachangabe möglich (Kardinalität 0..n). @@ -165,7 +182,7 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld --- -## 2 — Inhaltliche Angaben +## 2 — Sprache & Übersetzungen ### In welcher Sprache sind die Daten? @@ -184,57 +201,6 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld **4 · Verständliche Langbeschreibung:** Geben Sie an, in welcher Sprache die Inhalte der Daten verfasst sind (z. B. die Spaltenüberschriften und Texte). Das hilft Nutzenden einzuschätzen, ob sie die Daten verstehen. Beispiel: Deutsch, Englisch. -### Mit welchen Schlagworten finde ich diese Daten? - -| Eigenschaft | Wert | -|---|---| -| DCAT-Property | `dcat:keyword` | -| Meta-Key | `_odw_keywords` | -| Stufe | Empfohlen | -| Vokabular | — | - -**1 · DCAT-AP-Frage:** Welche `dcat:keyword` (Schlagwörter) beschreiben den Datensatz? - -**2 · Verständliche Frage:** Mit welchen Schlagworten finde ich diese Daten? - -**3 · DCAT-AP-Langbeschreibung:** `dcat:keyword` sind freie, sprachlich getaggte Schlagwörter zur Verbesserung der Auffindbarkeit (`literal-lang`, Kardinalität 0..n). Anders als `dcat:theme` sind sie nicht an ein kontrolliertes Vokabular gebunden. Je Schlagwort ein Wert. - -**4 · Verständliche Langbeschreibung:** Tragen Sie einzelne Schlagwörter ein, unter denen man Ihre Daten suchen würde — jedes Wort in eine eigene Zeile. Sie ergänzen das Thema und machen die Daten leichter auffindbar. Beispiel: Umwelt, Wasser, Luftverschmutzung. - -### Wann wurden diese Daten zum ersten Mal veröffentlicht? - -| Eigenschaft | Wert | -|---|---| -| DCAT-Property | `dct:issued` | -| Meta-Key | `_odw_issued` | -| Stufe | Empfohlen | -| Vokabular | — | - -**1 · DCAT-AP-Frage:** Wann wurde der Datensatz `dct:issued` (erstveröffentlicht)? - -**2 · Verständliche Frage:** Wann wurden diese Daten zum ersten Mal veröffentlicht? - -**3 · DCAT-AP-Langbeschreibung:** `dct:issued` ist das Datum der formalen Erstveröffentlichung des Datensatzes, typisiert als `xsd:date` bzw. `xsd:dateTime`. Kardinalität 0..1. Nicht zu verwechseln mit `dct:modified` (letzte Änderung) oder dem Erhebungszeitraum (`dct:temporal`). - -**4 · Verständliche Langbeschreibung:** Geben Sie das Datum an, an dem die Daten erstmals veröffentlicht wurden. Das ist der „Geburtstag" des Datensatzes, nicht der Zeitraum, den die Daten abdecken. Beispiel: 2024-01-15. - -### Wann wurden diese Daten zuletzt aktualisiert? - -| Eigenschaft | Wert | -|---|---| -| DCAT-Property | `dct:modified` | -| Meta-Key | `_odw_modified` | -| Stufe | Empfohlen | -| Vokabular | — | - -**1 · DCAT-AP-Frage:** Wann wurde der Datensatz zuletzt `dct:modified` (geändert)? - -**2 · Verständliche Frage:** Wann wurden diese Daten zuletzt aktualisiert? - -**3 · DCAT-AP-Langbeschreibung:** `dct:modified` gibt das Datum der letzten inhaltlichen Änderung des Datensatzes an (`xsd:date`/`xsd:dateTime`, Kardinalität 0..1). Das Plugin setzt diesen Wert bei jeder Speicherung automatisch; ein Eingabefeld gibt es bewusst nicht, da eine manuelle Angabe beim nächsten Speichern überschrieben würde. - -**4 · Verständliche Langbeschreibung:** Das Datum der letzten Aktualisierung. Es wird beim Speichern automatisch gesetzt, sodass Nutzende immer sehen, wie aktuell die Daten sind — Sie müssen dafür nichts eintragen. Beispiel: 2026-04-22. - --- ## 3 — Datenbereitstellung @@ -379,6 +345,40 @@ Legende der Stufen-Spalte: **Pflicht** (Veröffentlichung wird ohne dieses Feld **4 · Verständliche Langbeschreibung:** Verlinken Sie eine Webseite, auf der man mehr über die Daten oder das Projekt erfährt — etwa eine Projekt- oder Dokumentationsseite. Das ist nicht der direkte Download, sondern die „Über uns"-Seite zu den Daten. Beispiel: https://beispiel.de/projekt. +### Wann wurden diese Daten zum ersten Mal veröffentlicht? + +| Eigenschaft | Wert | +|---|---| +| DCAT-Property | `dct:issued` | +| Meta-Key | `_odw_issued` | +| Stufe | Empfohlen | +| Vokabular | — | + +**1 · DCAT-AP-Frage:** Wann wurde der Datensatz `dct:issued` (erstveröffentlicht)? + +**2 · Verständliche Frage:** Wann wurden diese Daten zum ersten Mal veröffentlicht? + +**3 · DCAT-AP-Langbeschreibung:** `dct:issued` ist das Datum der formalen Erstveröffentlichung des Datensatzes, typisiert als `xsd:date` bzw. `xsd:dateTime`. Kardinalität 0..1. Nicht zu verwechseln mit `dct:modified` (letzte Änderung) oder dem Erhebungszeitraum (`dct:temporal`). + +**4 · Verständliche Langbeschreibung:** Geben Sie das Datum an, an dem die Daten erstmals veröffentlicht wurden. Das ist der „Geburtstag" des Datensatzes, nicht der Zeitraum, den die Daten abdecken. Beispiel: 2024-01-15. + +### Wann wurden diese Daten zuletzt aktualisiert? + +| Eigenschaft | Wert | +|---|---| +| DCAT-Property | `dct:modified` | +| Meta-Key | `_odw_modified` | +| Stufe | Empfohlen | +| Vokabular | — | + +**1 · DCAT-AP-Frage:** Wann wurde der Datensatz zuletzt `dct:modified` (geändert)? + +**2 · Verständliche Frage:** Wann wurden diese Daten zuletzt aktualisiert? + +**3 · DCAT-AP-Langbeschreibung:** `dct:modified` gibt das Datum der letzten inhaltlichen Änderung des Datensatzes an (`xsd:date`/`xsd:dateTime`, Kardinalität 0..1). Das Plugin setzt diesen Wert bei jeder Speicherung automatisch; ein Eingabefeld gibt es bewusst nicht, da eine manuelle Angabe beim nächsten Speichern überschrieben würde. + +**4 · Verständliche Langbeschreibung:** Das Datum der letzten Aktualisierung. Es wird beim Speichern automatisch gesetzt, sodass Nutzende immer sehen, wie aktuell die Daten sind — Sie müssen dafür nichts eintragen. Beispiel: 2026-04-22. + ### Wie oft werden diese Daten aktualisiert? | Eigenschaft | Wert | diff --git a/includes/class-admin.php b/includes/class-admin.php index c51280a8..a73742a2 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -750,11 +750,11 @@ public static function render_introduction_page(): void {

  1. - +
  2. -
    - +
    +

  3. @@ -762,7 +762,7 @@ public static function render_introduction_page(): void {

  4. - +

  5. diff --git a/includes/class-fields.php b/includes/class-fields.php index ceb29ec8..8a14a160 100644 --- a/includes/class-fields.php +++ b/includes/class-fields.php @@ -47,7 +47,7 @@ public static function register(): void { * Registers the tabbed meta container with all dataset fields. * Tab structure (v2.1+): * 1 — Grundlegende Informationen - * 2 — Inhaltliche Angaben + * 2 — Sprache & Übersetzungen * 3 — Datenbereitstellung (Lizenz + Distribution) * 4 — Erweiterte Angaben * 5 — Vorschau @@ -87,14 +87,23 @@ private static function register_required_fields(): void { ->add_options( self::get_theme_options() ) ->set_help_text( __( 'THEMA (dcat:theme)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Umwelt, Bildung, Gesundheit, Wirtschaft, Kultur', 'open-data-wizard' ) ), - // Weniger häufige Einordnungen (CESSDA, ZiviZ) und die Übersetzungen - // als aufklappbare Untergruppe ans Tab-Ende — entschlackt den Einstieg - // für die typische Nutzung (B3, Accordion-Muster wie Tab 4). + // Schlagworte gehören zur inhaltlichen Erschließung und stehen + // deshalb bei Thema, CESSDA und Engagementfeld statt bei Sprache + // und Übersetzungen. Sie bleiben oberhalb der aufklappbaren + // Untergruppe sichtbar — an ihnen hängen 30 MQA-Punkte. + Field::make( 'textarea', 'odw_keywords', __( 'Mit welchen Schlagworten finde ich diese Daten?', 'open-data-wizard' ) ) + ->set_rows( 3 ) + ->set_attribute( 'placeholder', __( "Umwelt\nWasser\nLuftverschmutzung", 'open-data-wizard' ) ) + ->set_help_text( __( 'SCHLAGWORTE (dcat:keyword)', 'open-data-wizard' ) . "\n\n" . __( "Jedes Schlagwort in eine eigene Zeile (nicht mit Komma trennen). Beispiel:\nUmwelt\nWasser\nLuftverschmutzung", 'open-data-wizard' ) ), + + // Weniger häufige Einordnungen (CESSDA, ZiviZ) als aufklappbare + // Untergruppe ans Tab-Ende — entschlackt den Einstieg für die + // typische Nutzung (B3, Accordion-Muster wie Tab 4). Field::make( 'html', 'odw_hint_tab1_extra' ) ->set_html( '' ), @@ -111,8 +120,27 @@ private static function register_required_fields(): void { ->set_attribute( 'placeholder', __( 'Engagementfeld eintippen oder auswählen…', 'open-data-wizard' ) ) ->set_help_text( __( 'ENGAGEMENTFELD (ZiviZ, dct:subject)', 'open-data-wizard' ) . "\n\n" . __( 'Optional: Ordnen Sie den Datensatz einem Engagementfeld der Zivilgesellschaft nach dem ZiviZ-Vokabular zu. Feld aus der Liste wählen — die zugehörige URI wird automatisch verwendet. Beispiel: Kultur, Sport, Umwelt- und Naturschutz.', 'open-data-wizard' ) ), + ) + ) + + // ----------------------------------------------------------------- + // Tab 2 — Sprache & Übersetzungen + // + // Trägt alles Sprachbezogene an einem Ort: die Hauptsprache des + // Datensatzes und sämtliche Übersetzungs-Repeater. Titel- und + // Beschreibungsübersetzungen standen vorher in Tab 1, die + // Schlagwortübersetzungen hier — getrennt von ihresgleichen. + // ----------------------------------------------------------------- + ->add_tab( + __( '2 — Sprache & Übersetzungen', 'open-data-wizard' ), + array( + Field::make( 'select', 'odw_language', __( 'In welcher Sprache sind die Daten?', 'open-data-wizard' ) ) + ->set_default_value( class_exists( 'ODW_Settings' ) ? (string) ODW_Settings::get( 'default_language' ) : '' ) + ->add_options( self::get_language_options() ) + ->set_help_text( __( 'SPRACHE (dct:language)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Deutsch, Englisch', 'open-data-wizard' ) ), + Field::make( 'html', 'odw_hint_translations' ) - ->set_html( '

    ' . esc_html__( 'Übersetzungen (optional)', 'open-data-wizard' ) . '

    ' . esc_html__( 'Titel und Beschreibung zusätzlich in weiteren Sprachen — für mehrsprachige, DCAT-AP-konforme Metadaten. Die Angaben oben bleiben die Hauptsprache.', 'open-data-wizard' ) . '

    ' ), + ->set_html( '

    ' . esc_html__( 'Übersetzungen (optional)', 'open-data-wizard' ) . '

    ' . esc_html__( 'Titel, Beschreibung und Schlagworte zusätzlich in weiteren Sprachen — für mehrsprachige, DCAT-AP-konforme Metadaten. Die Angaben in Tab 1 bleiben die Hauptsprache.', 'open-data-wizard' ) . '

    ' ), Field::make( 'complex', 'odw_title_translations', __( 'Titel in weiteren Sprachen', 'open-data-wizard' ) ) ->set_collapsed( true ) @@ -154,24 +182,6 @@ private static function register_required_fields(): void { ) ) ->set_header_template( '<%- content || "' . esc_js( __( 'Übersetzung', 'open-data-wizard' ) ) . '" %>' ), - ) - ) - - // ----------------------------------------------------------------- - // Tab 2 — Inhaltliche Angaben - // ----------------------------------------------------------------- - ->add_tab( - __( '2 — Inhaltliche Angaben', 'open-data-wizard' ), - array( - Field::make( 'select', 'odw_language', __( 'In welcher Sprache sind die Daten?', 'open-data-wizard' ) ) - ->set_default_value( class_exists( 'ODW_Settings' ) ? (string) ODW_Settings::get( 'default_language' ) : '' ) - ->add_options( self::get_language_options() ) - ->set_help_text( __( 'SPRACHE (dct:language)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Deutsch, Englisch', 'open-data-wizard' ) ), - - Field::make( 'textarea', 'odw_keywords', __( 'Mit welchen Schlagworten finde ich diese Daten?', 'open-data-wizard' ) ) - ->set_rows( 3 ) - ->set_attribute( 'placeholder', __( "Umwelt\nWasser\nLuftverschmutzung", 'open-data-wizard' ) ) - ->set_help_text( __( 'SCHLAGWORTE (dcat:keyword)', 'open-data-wizard' ) . "\n\n" . __( "Jedes Schlagwort in eine eigene Zeile (nicht mit Komma trennen). Beispiel:\nUmwelt\nWasser\nLuftverschmutzung", 'open-data-wizard' ) ), Field::make( 'complex', 'odw_keyword_translations', __( 'Schlagworte in weiteren Sprachen', 'open-data-wizard' ) ) ->set_collapsed( true ) @@ -194,29 +204,6 @@ private static function register_required_fields(): void { ) ->set_header_template( '<%- language || "' . esc_js( __( 'Übersetzung', 'open-data-wizard' ) ) . '" %>' ), - Field::make( 'date', 'odw_issued', __( 'Wann wurden diese Daten zum ersten Mal veröffentlicht?', 'open-data-wizard' ) ) - ->set_storage_format( 'Y-m-d' ) - ->set_picker_options( - array( - 'dateFormat' => 'Y-m-d', - 'locale' => 'de', - ) - ) - ->set_help_text( __( 'VERÖFFENTLICHUNGSDATUM (dct:issued)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: 2024-01-15', 'open-data-wizard' ) ), - - // Das Änderungsdatum (dct:modified) hat kein Eingabefeld mehr: - // ODW_Fields::set_modified_date() überschreibt _odw_modified bei - // jedem Speichern, eine Eingabe ging also kommentarlos verloren. - // Ein gesperrtes Feld mitsamt „Datum wählen"-Button vorzuhalten, - // das niemand bedienen kann, ist Ballast. Der Wert bleibt in der - // Übersichtsspalte „Änderungsdatum", im Qualitätsbericht und in - // der JSON-LD-Vorschau sichtbar. - Field::make( 'html', 'odw_hint_modified' ) - ->set_html( - '

    ' - . esc_html__( 'Das Änderungsdatum (dct:modified) wird bei jedem Speichern automatisch gesetzt.', 'open-data-wizard' ) - . '

    ' - ), ) ) @@ -356,6 +343,33 @@ private static function register_required_fields(): void { ->set_attribute( 'placeholder', 'https://beispiel.de/projekt' ) ->set_help_text( __( 'PROJEKTSEITE (dcat:landingPage)', 'open-data-wizard' ) . "\n\n" . __( 'URL der Projektwebsite oder des Datenportals mit weiteren Informationen zum Datensatz. Beispiel: https://beispiel.de/projekt', 'open-data-wizard' ) ), + // Erstveröffentlichung und Aktualisierungsfrequenz beschreiben + // beide die Aktualität des Datensatzes und stehen deshalb hier + // beisammen — unter „Inhaltliche Angaben" war das Datum fehl am Platz. + Field::make( 'date', 'odw_issued', __( 'Wann wurden diese Daten zum ersten Mal veröffentlicht?', 'open-data-wizard' ) ) + ->set_storage_format( 'Y-m-d' ) + ->set_picker_options( + array( + 'dateFormat' => 'Y-m-d', + 'locale' => 'de', + ) + ) + ->set_help_text( __( 'VERÖFFENTLICHUNGSDATUM (dct:issued)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: 2024-01-15', 'open-data-wizard' ) ), + + // Das Änderungsdatum (dct:modified) hat kein Eingabefeld mehr: + // ODW_Fields::set_modified_date() überschreibt _odw_modified bei + // jedem Speichern, eine Eingabe ging also kommentarlos verloren. + // Ein gesperrtes Feld mitsamt „Datum wählen"-Button vorzuhalten, + // das niemand bedienen kann, ist Ballast. Der Wert bleibt in der + // Übersichtsspalte „Änderungsdatum", im Qualitätsbericht und in + // der JSON-LD-Vorschau sichtbar. + Field::make( 'html', 'odw_hint_modified' ) + ->set_html( + '

    ' + . esc_html__( 'Das Änderungsdatum (dct:modified) wird bei jedem Speichern automatisch gesetzt.', 'open-data-wizard' ) + . '

    ' + ), + Field::make( 'select', 'odw_accrual_periodicity', __( 'Wie oft werden diese Daten aktualisiert?', 'open-data-wizard' ) ) ->add_options( self::get_periodicity_options() ) ->set_help_text( __( 'AKTUALISIERUNGSFREQUENZ (dct:accrualPeriodicity)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Täglich, Monatlich, Jährlich, Unregelmäßig', 'open-data-wizard' ) ), @@ -479,9 +493,15 @@ private static function register_required_fields(): void { . '' ), + // „Öffentlich“ ist bei einem Open-Data-Plugin der Normalfall und + // steht deshalb vorausgewählt — sichtbar im Formular und jederzeit + // änderbar. Bewusst kein Hintergrund-Automatismus aus dem + // Beitragsstatus: Metadaten, die niemand gesehen hat, sollen nicht + // still entstehen. Field::make( 'select', 'odw_access_rights', __( 'Wer darf auf diese Daten zugreifen?', 'open-data-wizard' ) ) ->add_options( self::get_access_rights_options() ) - ->set_help_text( __( 'ZUGRIFFSRECHTE (dct:accessRights)', 'open-data-wizard' ) . "\n\n" . __( 'Zugriffs-Klassifikation des Datensatzes. Beispiel: Öffentlich, Eingeschränkt, Nicht öffentlich', 'open-data-wizard' ) ), + ->set_default_value( 'http://publications.europa.eu/resource/authority/access-right/PUBLIC' ) + ->set_help_text( __( 'ZUGRIFFSRECHTE (dct:accessRights)', 'open-data-wizard' ) . "\n\n" . __( 'Zugriffs-Klassifikation des Datensatzes. Vorbelegt mit „Öffentlich“ — bitte ändern, wenn die Daten nur eingeschränkt oder gar nicht öffentlich zugänglich sind.', 'open-data-wizard' ) ), Field::make( 'text', 'odw_theme_uri', __( 'Weiteres EU-Thema (Themen-URI)?', 'open-data-wizard' ) ) ->set_attribute( 'data-odw-vocab', 'data-theme' ) @@ -492,13 +512,18 @@ private static function register_required_fields(): void { ->set_html( '' ), - Field::make( 'select', 'odw_is_hvd', __( 'Ist dies ein hochwertiger Datensatz (HVD)?', 'open-data-wizard' ) ) + // HVD ist eine Rechtskategorie der EU-Durchführungsverordnung + // 2023/138 und richtet sich an öffentliche Stellen. Für Vereine + // und Verbände ist sie praktisch nie einschlägig, für Kommunen + // dagegen schon — deshalb bleiben die Felder, sagen aber selbst, + // für wen sie gedacht sind. + Field::make( 'select', 'odw_is_hvd', __( 'Nur für öffentliche Stellen: Ist dies ein hochwertiger Datensatz (HVD)?', 'open-data-wizard' ) ) ->add_options( self::get_hvd_flag_options() ) - ->set_help_text( __( 'HIGH-VALUE-DATENSATZ (EU-Durchführungsverordnung 2023/138)', 'open-data-wizard' ) . "\n\n" . __( 'Hochwertige Datensätze (HVD) sind von der EU festgelegte Datensätze mit besonderem Nutzen für Wirtschaft und Gesellschaft. Falls zutreffend, wählen Sie unten die passende Kategorie.', 'open-data-wizard' ) ), + ->set_help_text( __( 'HIGH-VALUE-DATENSATZ (EU-Durchführungsverordnung 2023/138)', 'open-data-wizard' ) . "\n\n" . __( 'Betrifft ausschließlich öffentliche Stellen — Vereine, Verbände und andere zivilgesellschaftliche Organisationen können dieses Feld leer lassen. Hochwertige Datensätze (HVD) sind von der EU festgelegte Datensätze mit besonderem Nutzen für Wirtschaft und Gesellschaft. Falls zutreffend, wählen Sie unten die passende Kategorie.', 'open-data-wizard' ) ), Field::make( 'select', 'odw_hvd_category', __( 'Welcher HVD-Kategorie gehört dieser Datensatz an?', 'open-data-wizard' ) ) ->add_options( self::get_hvd_category_options() ) diff --git a/includes/class-validation.php b/includes/class-validation.php index 22c4dc7e..e7727dd8 100644 --- a/includes/class-validation.php +++ b/includes/class-validation.php @@ -218,7 +218,7 @@ private static function error( string $key, string $fallback = '' ): array { private static function tab_name( int $tab ): string { $names = array( 1 => __( 'Grundlegende Informationen', 'open-data-wizard' ), - 2 => __( 'Inhaltliche Angaben', 'open-data-wizard' ), + 2 => __( 'Sprache & Übersetzungen', 'open-data-wizard' ), 3 => __( 'Datenbereitstellung', 'open-data-wizard' ), 4 => __( 'Erweiterte Angaben', 'open-data-wizard' ), 5 => __( 'Vorschau', 'open-data-wizard' ), diff --git a/languages/open-data-wizard-en_US.mo b/languages/open-data-wizard-en_US.mo index b8248b95a7dec5354bbfc8a7d7e08062b0b1ba93..6893753eafa766c4cceb2fe1c2956dd58d0d30a0 100644 GIT binary patch delta 11458 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 Im)oWP0m6u0h5!Hn delta 10727 zcmZA630PIt`p5B&qJRv7iXsYbK^YVfr^G39HcNA!asoLBdIANM)NE^}=2Y3SOs&ka ze`=}av0GM_Wlou9b}LPDEcJ?&=3I8W-yduL?{lA?=RWtd);jyF^{#ilYXi1se&F`m zfln`X>ROg{DAls$MU26~CYBX}ahQo|*bYZwD|{Y9@PO-g*qHul48x$NmNgh_VI7=+ zk@y&nM4vl;xvAw?4HyVcv#dl+!BD&(JK->l#piJ?`f;?N-A?R6hgx$m)Y(I1_8(Lf6%jN4!890qf?RC_dS0fz> z*cel=7uLnes0kOLcKkZV;BKsoCsEJe#u}JF0H4`UeeRWZ1o`+mpW zppAc`jwZghd7)*ZGCT-%E4-*%wj5u<4;>mRk}>yL)&n>LAH@BrGfcjpyNp?=H`YC< z3HxF&=Ai;BLd`P=r{ii=AhEn{^!+qc23nyq+ZDsnDWVZVV&(B1y28VK|<^5YGA%D$pD3vO4{0TroE2Sh4-h!cDO%541#0 z)DEL?7-rxEOve?d=k}mdc@VYptElIy4=~sCZfr(>G%6!c;#U|#foVg>ut@j6)gV(W zYf&lv990uvV=asxY>KZ5R-<2nDxycR39i7(_!&myUU&QiR@{~$=7&-u>V-B2m6^vd zo%OA!Y3Qs!LM`wmYJu-D6tAOp7%4Q;fhKSOW*+Hq1xuFoFMTgELUy_oD** z61Cn>=+vfhgN9NYKHOAsdsL(&QMKWq&Tau}f)`N>`cWC!joQ%_)IxuuZbiKjCeW_f zoc?HRk5A)k_{9kFKY>Q^gIp2(0b5~>2jqZXcq3S;hGv_c$Dtxl#y;2; zJK<7P=DtE3lX6TZdZKR0U{r=ix=uzN%{)v&XA=$W_&e0EVAn7fW5$`YZHW!&k49am zM^Qz(31e^@4#dOA4YZPS&F2rJHZTVh@mbUnZ9^T+9;A33>lh8{#kz=L*v(^p4EM)< z0Tc^v!4cz40O1AZo_9nA;>8dwL!JF>Y=tYZ5gtU{f~%+ino@8o-nJO1`+q+TeX&2* z#RAls&Ov3uhf4V-q;Rb}sFV(!$WJu%;yw5(>N@_2*YGMTfD2xJNx|@k%(Z(5Q|a%P ztZ!YWk$|y<=FQg;`8i}gi0Qc2^)T{3y|ygs$tprl%R1=Vd=fta=`X__IBPQR2JBpH zj^YXIM}H+MuyS;?KxB#g%EVCmqfym80rjFN#t58)Ha>&l=tu436V%a_qcV026}csYhz_*Rc2yQ<4I^^ zXV)B5fOBv$E=KLN?lg1F5>bKPj|ylk-i;*=4MnmAmGTRy1uvr(3Yu=JHvyZ|AAwzQ zF%H5*sO#C7w~LClJL<>=qcWF=3S=SbeXz_OUxlg#=VKauXhc3@?(b++gm0lTvJ>^i zbND6xjFWKtOjAS|kD9*+2BN+<9Q8dfK8{bhpVuPOdLP81HkgLggkyE35zD|Nd=3|( zQkgKzyz!bMo3wgjJNy)h!TQtfPoHi2@1Qc%;xRMdR8(;HmeyZz&+1q0@qvu}ur^jo98 zKO7@j-^!-Z5R0)rE_3^bP-prZYJp1g%-JR*r)FiLs(dRdV`ni8FQHO=+ck2&$wV@? zVSF&A;u3V4(0G@I0yu-Z4S`RX3F@L2$aMQ-FoFJ5)Xr8QXJ~Ch1%3_{Xu<;Xs_u!( zU>UZ>^{D61q5_FrNd8;W=(W%+^av)?e*;_K*Vq-SE;6a>jrHjl;t*Vf<#-0;@Ze(e z+wCP(fH6z>`GHxejqO9tdjg9v>Phmi$Y(!kQnVdY=$E_wT2JwJ9Q`gh5tpDQx{dJ| z!R+^9I`+XbjK*E4qCSSo;0@G{8!t7#7mPw>YL!DnMY98y+UppGG0&JUHbw8!z6qM70_|idQRvvQ#=`{l(j({hhj~97^mW5 z%*M*kngBf*LBAMvB(qTgzJ%3rFWPtnqwqG?z)D9Xx;v=qJ<$Dm`zCt~P4CD~94&?4bMarJ;z{qt0|2 z#^4cD#8*&961c+bI106(jnUZ7wI6n&pNFvl%!g_8KU-;1e+6y&F{{jPOl@%_>sw1` zQUtk=bz<9iadamAT^W!`P6`%)6 zf;9t))vEn6`ENnP|FXHa=P-+YjWx#munGOgQB}Jcb#^Dvix*L~GV~Rbscck6yr_*m zjtXoMYM%9|!2XGhxnrH&579PV%xC)zL*jn>TYX@vj|2fnG zd$BEE#YUL^ni(H}>d!<4^a|dEI~*EkXxv2Y?EC*S51d0S9Qe9_ z+5NofI{tE|-vf07({T{4K&Ac{*TD7WXk)Po<4yvNx-?QS1-qb9JrT8|m6(dVupa*E z_Cq(ARJO*(jCVoRNWSYdRGfNC1w%3FP4kYA!@l(UVK%Nt1r)H+e7`n^>;9+F&==dG7Vd$1ALO8_ zyVxCn9MkE)j3Ias_4yH01}~y&CUBFPuRcc5PeC0`8_d9o7=$Y^jP@I<7+1z#Ev3hcO(j_e@bmpq@{|yRj{*h#yAP#u`-3 ze1_WbN!Kf=qOH7*{Oew(a+fsF7wcmw>IjyiCR~dux&x?*&!V2M`aZw&V;buDw{Q^d z#DN&|fmzs#f%G3mWpEB^Ba0mx+UZKv8ErrXuni0GThzqeKQwQ&VOW#?7)-_zR0dYL zpRdCz^f$Zxcd#M-53w7bKpk!3b`yxxj7B5_S=buy#d_$VQo0JY!1s6!&!Yl3{*n3B z>oRI5$3HezeG#kD{|)2uHfn=0J50@_xQ;}gbF8OnXeZ0D4t|VE`FE(u^)JgUAps}j z5PSjmVjuK;YW^(uV?+88J59iu*qMGutcDJD#D%B;_hOXp|CcnHFmM`mMq!_sh@((} zHAH3PUd%!dYN3}Oty0;z?1?jF<;j6?-619cR0QMc|{bTrWh8j1J; zYDXtg6JNsmSZS}>Ndru!-v(P?HZHU-fa#s{NP+x!UW z#rseTpF>soO-x4ns7ZY)RLg;h`Cv-l=X zJ#OCJi9eWy^D&F@X{h_X1yuuIVFSE_I->d~Oh%JY{bs1lv_l>F1L#zzF`kBYQidwJ zm$5Ex#38r`b#LQOnuQvpj;bH(?1!O_EDy)xbW}!(Uim@ZkKrOV*{k$CG>EA)kSMQA3X*1WJsPB()`}t=a z^EcTncVG=_XWK9zKfw@ea@G`4E7aNF=Qc}pmiaPa2 z^V4;tL*sr1R-ssH5nQF*ppn<7DiC?_w|fH!8(F&zZnR zpuRT^Kf+m9h)$>T=0&mzt1$2l>WjxvHSi1OV(F!|9e1I=cNOo#nm;R}oPTc`+Ua0yh()LXmZBE^-1P!#=T(1k|Bgp3 z)D{(3SFDE*U`w2cI{VeA=iW!n_nF&2gI?CRF40KB;lG+4&B6%!Phd1IN2PirCg2Wi zjAv03hFmr;m}FED_D2=pBvg$&ftk1lb^i}zBA!RbrV)C@ys^?yN6{ZO@gmH?^{z*- z4gH|s%$aw>4)nd)LeF6e9>*qF<*NB>ITIDYSX8E$y1sFh{10bf7Xw^btHCvM<^k8u zQN&;xBeujFn2$|vn7$8{$vvo}`~_75kvGjh({;eH^yi_D?EFpgUr`hc z=*(;0GS}#S)IvT~%3ee5;C<9i%Tbw%zim=J3|rGLL)E}Wtcv?E9S@;0^KVpUl7BbP z4RC1K3>2bL{uH*wji|`aqXMk?hxtNF)EPg3jj#}H^r3dN6=QIRJAM>ZOTS|thTJi~ zHBZ87^qr4rgwxoE3gA1`#Mj;N*gs85Q&0;$fHmnrJ81!TqjhQ9JnqwP3BkOe#|_j{Xpg#8T{ub5Tcj0Bt;pwUNL0 zE1s)^3Md7`uoc$PJGV29Xaf^qpCyK~|A>>U54 zR43H`;aycD{mptFjq)`ap7_jN@B8}=@L{vSqMkMh@lsC}@n zsA(Pl+$lS%_(soISJf^lDl72#f*wf;$nam8@kwBmJ*6<$?&2vKmtX8D$eB_&-gjtL zPGC`vr_kSSc8B1|BzxxzIiBJYPw8|%WOT}c^Ob!ImSonq|Nc>eojqk-zU?U}^!PF# ziB3*a)9%hD#(N&}6qe?Bytx(2{cXnbB@=>um*!Q=*!f&u9?xm>X?FJlmQz?e*A`AJ zO|$zlT59k7$A2I3fBW3Zpop$Tg{6GQ?&c}YF7W#2z0|LozsiPy2*3BeN|7~(WP7K0 z>~7hm*=)d0={2NVs&Dn)Sl@%|Q)}fBXRe(y%`PR?-3Qxc1*Q4ESJro`TH^6~#+CZc zuaEa{+dDj<`u{JHTd_omFY5E;fO~!0k0$!FJ};>5KXr6TkblR4U2v$Di*T7+Eox%WjGbCY2Tx75e&?$N1Kj#|NbQ_LSRIvU7^2di=kZ=LSV2 z*`>wVg(co>Jyzl$aJ8Uv&4Lm;mmPGnhvsLO+Vl!aeTQ#k`ciHtbWCGv+wpA{NE)L?D54#Qzm6tL-UJ#+i%AB{Wqrv n`O1F|iSIWhhm%RO2Y5 { await page.goto(`${BASE_URL}/wp-admin/post-new.php?post_type=odw_dataset`); // Click Tab 2 - const tab2Button = page.locator('.cf-container__tabs-nav >> text="2 — Inhaltliche Angaben"'); + const tab2Button = page.locator('.cf-container__tabs-nav >> text="2 — Sprache & Übersetzungen"'); if (await tab2Button.count() > 0) { await tab2Button.click(); // Verify Tab 2 is now active const activeTab = page.locator('.cf-container__tabs-nav li.cf-tab--active'); - await expect(activeTab).toContainText('2 — Inhaltliche Angaben'); + await expect(activeTab).toContainText('2 — Sprache & Übersetzungen'); } }); From f554d4156316d378d4c7b984a849b19058f584f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 11:34:42 +0000 Subject: [PATCH 2/2] feat: Datei-Upload aus der Seitenleiste ins Formular holen (v2.35.8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Upload stand als eigene Meta-Box in der Seitenleiste, während die Zugriffs-URL in Tab 3 lag — zwei Orte für denselben Sachverhalt, die zu widersprüchlichen Angaben einluden, ohne dass die Oberfläche den Zusammenhang zeigte. Das Widget steht jetzt unmittelbar unter der Zugriffs-URL, die Hilfetexte beider Felder verweisen aufeinander. Umsetzung: - render_file_meta_box() wird zu file_upload_html() und liefert Markup als String statt es auszugeben. - Eingehängt als Carbon-Fields-html-Feld mit Callback. Html_Field::to_json() ruft den Callback erst beim Rendern auf, dort steht der Beitragskontext zur Verfügung — der Dateizustand kommt also weiterhin vom Server und hängt nicht daran, dass JavaScript ihn nachträgt. - odw-file-upload.js nutzt delegierte Handler an document. Carbon Fields hängt Felder erst nach DOMContentLoaded ein und kann sie neu rendern; direkt gebundene Handler hätten ins Leere gegriffen. - save_file_attachment() unverändert; bricht weiterhin ohne Nonce ab, eine bestehende Verknüpfung geht also auch dann nicht verloren, wenn das Widget einmal nicht rendert. Verifiziert: php -l, node --check, Katalog-Abdeckung, check-i18n.py bei 0, Feld-Referenz unverändert. Die Interaktion mit wp.media lässt sich nur in einer laufenden Instanz prüfen — Auswahl, Speichern und Entfernen bitte einmal manuell testen. https://claude.ai/code/session_01JB1xUQM892bVZ4Yv3MZjvq --- CHANGELOG.md | 24 ++++++ CLAUDE.md | 6 +- README.md | 4 +- assets/js/odw-file-upload.js | 123 ++++++++++++++-------------- includes/class-admin.php | 44 +++++----- includes/class-fields.php | 17 +++- languages/open-data-wizard-en_US.mo | Bin 66465 -> 66549 bytes languages/open-data-wizard-en_US.po | 11 +-- languages/open-data-wizard.pot | 5 +- open-data-wizard.php | 4 +- 10 files changed, 136 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f379957e..fe166ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,30 @@ 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. diff --git a/CLAUDE.md b/CLAUDE.md index 873a914f..7f421f32 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -142,7 +142,7 @@ Externe Harvester rufen /catalog, /datasets/, 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()` | @@ -722,7 +722,7 @@ Update **both** locations: - MINOR: New features (backward-compatible) - PATCH: Bug fixes only -Current: **v2.35.7** +Current: **v2.35.8** --- @@ -861,6 +861,6 @@ Neue technische Festlegungen gehören dorthin, nicht in README oder CLAUDE.md. --- -**Zuletzt aktualisiert**: Version 2.35.7 (August 2026) +**Zuletzt aktualisiert**: Version 2.35.8 (August 2026) **Autor**: Open Data Wizard Team (nozilla) **License**: GPL-2.0-or-later diff --git a/README.md b/README.md index 38cb90e3..281cc8dc 100644 --- a/README.md +++ b/README.md @@ -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.7-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) @@ -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')` diff --git a/assets/js/odw-file-upload.js b/assets/js/odw-file-upload.js index bcfdc362..dcca71c0 100644 --- a/assets/js/odw-file-upload.js +++ b/assets/js/odw-file-upload.js @@ -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 ); diff --git a/includes/class-admin.php b/includes/class-admin.php index a73742a2..80bec83c 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -48,7 +48,10 @@ public static function init(): void { add_action( 'add_meta_boxes', array( self::class, 'register_help_tabs' ) ); add_action( 'load-post.php', array( self::class, 'register_help_tabs' ) ); add_action( 'load-post-new.php', array( self::class, 'register_help_tabs' ) ); - add_action( 'add_meta_boxes', array( self::class, 'register_file_meta_box' ) ); + // Der Datei-Upload ist keine eigene Meta-Box mehr, sondern steht als + // html-Feld direkt unter der Zugriffs-URL in Tab 3 (siehe ODW_Fields). + // Zwei getrennte Orte für denselben Sachverhalt luden zu widersprüchlichen + // Angaben ein — Link hier, Datei dort. // Auf 'save_post'@20 (nicht 'save_post_odw_dataset'): Carbon Fields // speichert auf 'save_post'@10, und save_post_{post_type} feuert davor. // Nur so bleibt die aus dem Upload abgeleitete Zugriffs-URL erhalten und @@ -480,30 +483,27 @@ public static function enqueue_assets( string $hook ): void { } // ------------------------------------------------------------------------- - // Download-Datei — Native Media Library Meta Box + // Download-Datei — Native Media Library // ------------------------------------------------------------------------- /** - * Register the file-upload meta box on the dataset edit screen. - */ - public static function register_file_meta_box(): void { - add_meta_box( - 'odw-file-upload', - __( 'Download-Datei (Mediathek)', 'open-data-wizard' ), - array( self::class, 'render_file_meta_box' ), - 'odw_dataset', - 'side', - 'default' - ); - } - - /** - * Render the file-upload meta box. + * Build the file-upload widget markup for the wizard form. * - * @param \WP_Post $post Current post object. + * Wird als Callback an ein Carbon-Fields-html-Feld übergeben. Carbon Fields + * ruft den Callback erst beim Rendern des Containers auf (Html_Field::to_json()), + * daher steht hier der Beitragskontext zur Verfügung und der aktuelle + * Dateizustand kann serverseitig ausgegeben werden — die Auswahl hängt nicht + * davon ab, dass JavaScript sie nachträgt. + * + * @return string */ - public static function render_file_meta_box( \WP_Post $post ): void { - $file_id = (int) get_post_meta( $post->ID, '_odw_file_id', true ); + public static function file_upload_html(): string { + $post_id = (int) get_the_ID(); + if ( $post_id <= 0 ) { + return ''; + } + + $file_id = (int) get_post_meta( $post_id, '_odw_file_id', true ); $has_file = $file_id > 0; $file_name = ''; @@ -512,6 +512,7 @@ public static function render_file_meta_box( \WP_Post $post ): void { $file_name = $attachment instanceof \WP_Post ? $attachment->post_title : ''; } + ob_start(); wp_nonce_field( 'odw_save_file_attachment', 'odw_file_upload_nonce' ); ?>
    @@ -546,11 +547,12 @@ class="button button-link-delete"

    - +

    set_attribute( 'placeholder', 'https://beispiel.de/daten/datei.csv' ) ->set_attribute( 'type', 'url' ) - ->set_help_text( __( 'ZUGRIFFS-URL (dcat:accessURL)', 'open-data-wizard' ) . "\n\n" . __( 'Zwei Wege — einer genügt: Tragen Sie hier den Link zu Ihrer Datei ein ODER laden Sie die Datei in der Box „Download-Datei (Mediathek)" hoch. Bei einem Upload wird die URL beim Speichern automatisch übernommen; dieses Feld können Sie dann leer lassen.', 'open-data-wizard' ) ), + ->set_help_text( __( 'ZUGRIFFS-URL (dcat:accessURL)', 'open-data-wizard' ) . "\n\n" . __( 'Zwei Wege — einer genügt: Tragen Sie hier den Link zu Ihrer Datei ein ODER laden Sie die Datei direkt darunter aus der Mediathek hoch. Bei einem Upload wird die URL beim Speichern automatisch übernommen; dieses Feld können Sie dann leer lassen.', 'open-data-wizard' ) ), + + // Der Datei-Upload stand früher als eigene Meta-Box in der + // Seitenleiste — zwei Orte für denselben Sachverhalt, die zu + // widersprüchlichen Angaben einluden. Jetzt steht er unmittelbar + // unter der Zugriffs-URL, zu der er die Alternative ist. + // + // Als Closure statt als Array-Callable: is_callable() prüft beim + // Registrieren, ODW_Admin ist dann je nach Ladereihenfolge noch + // nicht bekannt. Der Aufruf selbst erfolgt erst beim Rendern. + Field::make( 'html', 'odw_file_upload' ) + ->set_html( + static function (): string { + return class_exists( 'ODW_Admin' ) ? ODW_Admin::file_upload_html() : ''; + } + ), Field::make( 'select', 'odw_format', __( 'In welchem Format ist die Datei?', 'open-data-wizard' ) ) ->add_options( self::get_format_options() ) diff --git a/languages/open-data-wizard-en_US.mo b/languages/open-data-wizard-en_US.mo index 6893753eafa766c4cceb2fe1c2956dd58d0d30a0..4318bf49d21ea591dba047e56c28e755eda034bb 100644 GIT binary patch delta 10529 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 delta 10465 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 YXy)^&@*R2EjPbZ0{CJv+OJSM+0&d8u^#A|> diff --git a/languages/open-data-wizard-en_US.po b/languages/open-data-wizard-en_US.po index dc5d63c5..088409a4 100644 --- a/languages/open-data-wizard-en_US.po +++ b/languages/open-data-wizard-en_US.po @@ -318,8 +318,8 @@ msgstr "The Problem" msgid "Das Wizard-Formular ist in fünf einfache Schritte unterteilt:" msgstr "The wizard form is divided into five simple steps:" -msgid "Datei aus der Mediathek verknüpfen — wird als Download-Button im [odw_dataset]-Shortcode angezeigt." -msgstr "Link a file from the media library — shown as a download button in the [odw_dataset] shortcode." +msgid "Alternative zur Zugriffs-URL oben: Datei aus der Mediathek verknüpfen. Die URL wird daraus automatisch gesetzt und die Datei als Download-Button im [odw_dataset]-Shortcode angezeigt." +msgstr "Alternative to the access URL above: link a file from the media library. The URL is set from it automatically and the file is shown as a download button in the [odw_dataset] shortcode." msgid "Datei auswählen / hochladen" msgstr "Select / upload file" @@ -480,9 +480,6 @@ msgstr "Primary distribution — further details" msgid "Distributionen" msgstr "Distributions" -msgid "Download-Datei (Mediathek)" -msgstr "Download file (media library)" - msgid "Download-URL" msgstr "Download URL" @@ -1681,8 +1678,8 @@ msgstr "To the dataset list" msgid "Zusammenfassung" msgstr "Summary" -msgid "Zwei Wege — einer genügt: Tragen Sie hier den Link zu Ihrer Datei ein ODER laden Sie die Datei in der Box „Download-Datei (Mediathek)\" hoch. Bei einem Upload wird die URL beim Speichern automatisch übernommen; dieses Feld können Sie dann leer lassen." -msgstr "Two ways — one is enough: enter the link to your file here OR upload the file in the “Download file (media library)” box. On upload the URL is filled in automatically on save; you can then leave this field empty." +msgid "Zwei Wege — einer genügt: Tragen Sie hier den Link zu Ihrer Datei ein ODER laden Sie die Datei direkt darunter aus der Mediathek hoch. Bei einem Upload wird die URL beim Speichern automatisch übernommen; dieses Feld können Sie dann leer lassen." +msgstr "Two ways — either will do: enter the link to your file here OR upload the file from the media library directly below. On upload the URL is taken over automatically when saving; you can then leave this field empty." msgid "Zweijährlich" msgstr "Biennial" diff --git a/languages/open-data-wizard.pot b/languages/open-data-wizard.pot index ed33d59f..ed9f4b44 100644 --- a/languages/open-data-wizard.pot +++ b/languages/open-data-wizard.pot @@ -210,7 +210,7 @@ msgstr "" msgid "Das Wizard-Formular ist in fünf einfache Schritte unterteilt:" msgstr "" -msgid "Datei aus der Mediathek verknüpfen — wird als Download-Button im [odw_dataset]-Shortcode angezeigt." +msgid "Alternative zur Zugriffs-URL oben: Datei aus der Mediathek verknüpfen. Die URL wird daraus automatisch gesetzt und die Datei als Download-Button im [odw_dataset]-Shortcode angezeigt." msgstr "" msgid "Datei auswählen / hochladen" @@ -333,9 +333,6 @@ msgstr "" msgid "Distribution mit Zugriffs-URL (dcat:accessURL)" msgstr "" -msgid "Download-Datei (Mediathek)" -msgstr "" - msgid "Download-URL, Format, Dateigröße, Lizenz und Zuschreibungstext." msgstr "" diff --git a/open-data-wizard.php b/open-data-wizard.php index 31d64d09..6b1c8a44 100644 --- a/open-data-wizard.php +++ b/open-data-wizard.php @@ -3,7 +3,7 @@ * Plugin Name: Open Data Wizard * Plugin URI: https://github.com/daimpad/OpenDataWizard * Description: DCAT-AP 3.0 konforme Open Data Metadatenverwaltung für WordPress. Bereitstellung als maschinenlesbarer JSON-LD-Endpoint für offene Daten. - * Version: 2.35.7 + * Version: 2.35.8 * Requires at least: 6.4 * Requires PHP: 8.1 * Author: nozilla @@ -26,7 +26,7 @@ exit; } -define( 'ODW_VERSION', '2.35.7' ); +define( 'ODW_VERSION', '2.35.8' ); define( 'ODW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'ODW_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'ODW_PLUGIN_FILE', __FILE__ );