diff --git a/CHANGELOG.md b/CHANGELOG.md index 226383c0..fe166ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/CLAUDE.md b/CLAUDE.md index a787a707..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()` | @@ -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.8** --- @@ -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 diff --git a/README.md b/README.md index 49f227f7..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.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) @@ -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 @@ -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/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/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/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..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"

- +


  • - +
  • -
    - +
    +

  • @@ -762,7 +764,7 @@ public static function render_introduction_page(): void {

  • - +

  • diff --git a/includes/class-fields.php b/includes/class-fields.php index ceb29ec8..127ba886 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' ) - . '

    ' - ), ) ) @@ -229,7 +216,22 @@ private static function register_required_fields(): void { Field::make( 'text', 'odw_access_url', __( 'Ergänzen Sie den Link zu Ihrem Datensatz oder laden Sie die Datei in die Mediathek hoch', 'open-data-wizard' ) ) ->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() ) @@ -356,6 +358,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 +508,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 +527,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 b8248b95..4318bf49 100644 Binary files a/languages/open-data-wizard-en_US.mo and b/languages/open-data-wizard-en_US.mo differ diff --git a/languages/open-data-wizard-en_US.po b/languages/open-data-wizard-en_US.po index 75d4d9da..088409a4 100644 --- a/languages/open-data-wizard-en_US.po +++ b/languages/open-data-wizard-en_US.po @@ -36,8 +36,8 @@ msgstr "(of max. %d)" msgid "1 — Grundlegende Informationen" msgstr "1 — Basic Information" -msgid "2 — Inhaltliche Angaben" -msgstr "2 — Content Details" +msgid "2 — Sprache & Übersetzungen" +msgstr "2 — Language & translations" msgid "3 — Datenbereitstellung" msgstr "3 — Data Provision" @@ -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" @@ -726,8 +723,8 @@ msgstr "Publisher (dct:publisher)" msgid "Herunterladen" msgstr "Download" -msgid "High-Value-Datensatz (HVD)" -msgstr "High-Value Dataset (HVD)" +msgid "High-Value-Datensatz (HVD) — nur für öffentliche Stellen" +msgstr "High-value dataset (HVD) — public sector bodies only" msgid "Hilfe anzeigen" msgstr "Show help" @@ -735,8 +732,8 @@ msgstr "Show help" msgid "Hinweis ausblenden" msgstr "Dismiss notice" -msgid "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." -msgstr "High-value datasets (HVD) are datasets defined by the EU with particular benefit to the economy and society. If applicable, select the matching category below." +msgid "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." +msgstr "Applies to public sector bodies only — associations, federations and other civil-society organisations can leave this field empty. High-value datasets (HVD) are datasets designated by the EU as being of particular benefit to the economy and society. If applicable, choose the matching category below." msgid "IANA-Media-Type als URI. Beispiel: https://www.iana.org/assignments/media-types/text/csv" msgstr "IANA media type as a URI. Example: https://www.iana.org/assignments/media-types/text/csv" @@ -777,8 +774,8 @@ msgstr "International Topics" msgid "Interoperabilität" msgstr "Interoperability" -msgid "Ist dies ein hochwertiger Datensatz (HVD)?" -msgstr "Is this a high-value dataset (HVD)?" +msgid "Nur für öffentliche Stellen: Ist dies ein hochwertiger Datensatz (HVD)?" +msgstr "Public sector bodies only: is this a high-value dataset (HVD)?" msgid "JSON-Beispiel" msgstr "JSON example" @@ -1135,8 +1132,8 @@ msgstr "Project page" msgid "Projektseite & Aktualität" msgstr "Project page & timeliness" -msgid "Projektseite, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen." -msgstr "Project page, update frequency, geographic and temporal coverage, contact information." +msgid "Projektseite, Erstveröffentlichung, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen." +msgstr "Project page, first publication, update frequency, geographic and temporal coverage, contact information." msgid "Punkte" msgstr "Points" @@ -1330,8 +1327,8 @@ msgstr "Title of the dataset (WordPress title field)" msgid "Titel in weiteren Sprachen" msgstr "Title in additional languages" -msgid "Titel und Beschreibung zusätzlich in weiteren Sprachen — für mehrsprachige, DCAT-AP-konforme Metadaten. Die Angaben oben bleiben die Hauptsprache." -msgstr "Title and description in additional languages — for multilingual, DCAT-AP-compliant metadata. The entries above remain the main language." +msgid "Titel, Beschreibung und Schlagworte zusätzlich in weiteren Sprachen — für mehrsprachige, DCAT-AP-konforme Metadaten. Die Angaben in Tab 1 bleiben die Hauptsprache." +msgstr "Title, description and keywords additionally in further languages — for multilingual, DCAT-AP compliant metadata. The entries in tab 1 remain the main language." msgid "Täglich" msgstr "Daily" @@ -1654,8 +1651,8 @@ msgstr "Access denied." msgid "Zugriffs- und Download-URLs beim Speichern per HTTP-HEAD auf Erreichbarkeit prüfen (MQA-Zugänglichkeit, +80 Punkte)." msgstr "Check access and download URLs for reachability via HTTP HEAD on save (MQA accessibility, +80 points)." -msgid "Zugriffs-Klassifikation des Datensatzes. Beispiel: Öffentlich, Eingeschränkt, Nicht öffentlich" -msgstr "Access classification of the dataset. Example: Public, Restricted, Non-public" +msgid "Zugriffs-Klassifikation des Datensatzes. Vorbelegt mit „Öffentlich“ — bitte ändern, wenn die Daten nur eingeschränkt oder gar nicht öffentlich zugänglich sind." +msgstr "Access classification of the dataset. Preset to \"Public\" — please change it if the data are only available on a restricted basis or not publicly at all." msgid "Zugriffs-URL" msgstr "Access 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" @@ -1781,8 +1778,8 @@ msgstr "HVD category" msgid "Grundlegende Informationen" msgstr "Basic information" -msgid "Inhaltliche Angaben" -msgstr "Content details" +msgid "Sprache & Übersetzungen" +msgstr "Language & translations" msgid "Datenbereitstellung" msgstr "Data provision" @@ -1799,8 +1796,8 @@ msgstr "Which theme is this dataset assigned to?" msgid "Mit welchen Schlagworten finde ich diese Daten?" msgstr "Which keywords help people find this data?" -msgid "Weitere Einordnung & Übersetzungen (optional)" -msgstr "Further classification & translations (optional)" +msgid "Weitere Einordnung (optional)" +msgstr "Further classification (optional)" msgid "Weiteres EU-Thema (Themen-URI)?" msgstr "Additional EU theme (theme URI)?" @@ -1832,11 +1829,11 @@ msgstr "Download JSON example" msgid "von" msgstr "of" -msgid "Sprache, Schlagworte sowie Veröffentlichungs- und Änderungsdatum." -msgstr "Language, keywords and the publication and modification dates." +msgid "Die Sprache der Daten und — falls gewünscht — Titel, Beschreibung und Schlagworte in weiteren Sprachen." +msgstr "The language of the data and — if desired — title, description and keywords in further languages." -msgid "Wer gibt diese Daten heraus? Worum geht es? Welchem Thema ist der Datensatz zugeordnet? Optional lassen sich unter „Weitere Einordnung & Übersetzungen\" auch eine Themenklassifikation, ein Engagementfeld und Übersetzungen ergänzen." -msgstr "Who publishes this data? What is it about? Which theme is the dataset assigned to? Optionally, a topic classification, an engagement field and translations can be added under the \"Further classification & translations\" group." +msgid "Wer gibt diese Daten heraus? Worum geht es? Welchem Thema ist der Datensatz zugeordnet? Und mit welchen Schlagworten findet man ihn? Optional lassen sich unter „Weitere Einordnung\" eine Themenklassifikation und ein Engagementfeld ergänzen." +msgstr "Who publishes these data? What are they about? Which theme is the dataset assigned to? And which keywords lead people to it? Optionally, a topic classification and a field of civic engagement can be added under \"Further classification\"." msgid "Die Pflichtfelder sind mit einem roten Sternchen (*) gekennzeichnet. Als Entwurf können Sie jederzeit unvollständig speichern – erst zum Veröffentlichen müssen alle Pflichtfelder ausgefüllt sein. Jedes Feld hat hilfreiche Beispiele, die Sie über das ⓘ-Symbol einblenden." msgstr "Required fields are marked with a red asterisk (*). You can always save an incomplete draft — only publishing requires all mandatory fields to be filled in. Every field has helpful examples that you can reveal via the ⓘ icon." diff --git a/languages/open-data-wizard.pot b/languages/open-data-wizard.pot index f17ae3a1..ed9f4b44 100644 --- a/languages/open-data-wizard.pot +++ b/languages/open-data-wizard.pot @@ -18,7 +18,7 @@ msgstr "" msgid "1 — Grundlegende Informationen" msgstr "" -msgid "2 — Inhaltliche Angaben" +msgid "2 — Sprache & Übersetzungen" msgstr "" msgid "3 — Datenbereitstellung" @@ -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 "" @@ -706,7 +703,7 @@ msgstr "" msgid "Projektseite & Aktualität" msgstr "" -msgid "Projektseite, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen." +msgid "Projektseite, Erstveröffentlichung, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen." msgstr "" msgid "Punkte" @@ -1024,16 +1021,16 @@ msgstr "" msgid "Ja, dieser Datensatz ist ein High-Value-Datensatz" msgstr "" -msgid "High-Value-Datensatz (HVD)" +msgid "High-Value-Datensatz (HVD) — nur für öffentliche Stellen" msgstr "" -msgid "Ist dies ein hochwertiger Datensatz (HVD)?" +msgid "Nur für öffentliche Stellen: Ist dies ein hochwertiger Datensatz (HVD)?" msgstr "" msgid "HIGH-VALUE-DATENSATZ (EU-Durchführungsverordnung 2023/138)" msgstr "" -msgid "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." +msgid "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." msgstr "" msgid "Welcher HVD-Kategorie gehört dieser Datensatz an?" @@ -1174,7 +1171,7 @@ msgstr "" msgid "ZUGRIFFSRECHTE (dct:accessRights)" msgstr "" -msgid "Zugriffs-Klassifikation des Datensatzes. Beispiel: Öffentlich, Eingeschränkt, Nicht öffentlich" +msgid "Zugriffs-Klassifikation des Datensatzes. Vorbelegt mit „Öffentlich“ — bitte ändern, wenn die Daten nur eingeschränkt oder gar nicht öffentlich zugänglich sind." msgstr "" msgid "Weiteres EU-Thema (Kategorie-URI)?" @@ -1405,7 +1402,7 @@ msgstr "" msgid "Grundlegende Informationen" msgstr "" -msgid "Inhaltliche Angaben" +msgid "Sprache & Übersetzungen" msgstr "" msgid "Datenbereitstellung" @@ -1423,7 +1420,7 @@ msgstr "" msgid "Mit welchen Schlagworten finde ich diese Daten?" msgstr "" -msgid "Weitere Einordnung & Übersetzungen (optional)" +msgid "Weitere Einordnung (optional)" msgstr "" msgid "Weiteres EU-Thema (Themen-URI)?" @@ -1456,10 +1453,10 @@ msgstr "" msgid "von" msgstr "" -msgid "Sprache, Schlagworte sowie Veröffentlichungs- und Änderungsdatum." +msgid "Die Sprache der Daten und — falls gewünscht — Titel, Beschreibung und Schlagworte in weiteren Sprachen." msgstr "" -msgid "Wer gibt diese Daten heraus? Worum geht es? Welchem Thema ist der Datensatz zugeordnet? Optional lassen sich unter „Weitere Einordnung & Übersetzungen\" auch eine Themenklassifikation, ein Engagementfeld und Übersetzungen ergänzen." +msgid "Wer gibt diese Daten heraus? Worum geht es? Welchem Thema ist der Datensatz zugeordnet? Und mit welchen Schlagworten findet man ihn? Optional lassen sich unter „Weitere Einordnung\" eine Themenklassifikation und ein Engagementfeld ergänzen." msgstr "" msgid "Die Pflichtfelder sind mit einem roten Sternchen (*) gekennzeichnet. Als Entwurf können Sie jederzeit unvollständig speichern – erst zum Veröffentlichen müssen alle Pflichtfelder ausgefüllt sein. Jedes Feld hat hilfreiche Beispiele, die Sie über das ⓘ-Symbol einblenden." diff --git a/open-data-wizard.php b/open-data-wizard.php index 95f58022..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.6 + * 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.6' ); +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__ ); diff --git a/tests/e2e/admin-workflow.spec.js b/tests/e2e/admin-workflow.spec.js index f022a0be..e8fc47d6 100644 --- a/tests/e2e/admin-workflow.spec.js +++ b/tests/e2e/admin-workflow.spec.js @@ -77,13 +77,13 @@ test.describe('Open Data Wizard Admin Workflow', () => { 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'); } });