-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclass-fields.php
More file actions
1693 lines (1470 loc) · 72.2 KB
/
Copy pathclass-fields.php
File metadata and controls
1693 lines (1470 loc) · 72.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/**
* Carbon Fields Felddefinitionen für odw_dataset
*
* @package OpenDataWizard
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Carbon_Fields\Container;
use Carbon_Fields\Field;
/**
* Registers all Carbon Fields field definitions for the odw_dataset post type.
*
* @package OpenDataWizard
*/
class ODW_Fields {
/**
* Registers WordPress hooks.
*/
public static function init(): void {
add_action( 'carbon_fields_register_fields', array( self::class, 'register' ) );
add_action( 'save_post_odw_dataset', array( self::class, 'set_modified_date' ), 10, 2 );
}
/**
* Entry point: registers all field groups.
*/
public static function register(): void {
self::register_required_fields();
self::register_optional_fields();
self::register_distributions();
}
/**
* Registers the tabbed meta container with all dataset fields.
* Tab structure (v2.1+):
* 1 — Grundlegende Informationen
* 2 — Inhaltliche Angaben
* 3 — Datenbereitstellung (Lizenz + Distribution)
* 4 — Erweiterte Angaben
* 5 — Vorschau
*/
private static function register_required_fields(): void {
Container::make( 'post_meta', __( 'Pflichtangaben', 'open-data-wizard' ) )
->where( 'post_type', '=', 'odw_dataset' )
->set_priority( 'high' )
// -----------------------------------------------------------------
// Tab 1 — Grundlegende Informationen
// -----------------------------------------------------------------
->add_tab(
__( '1 — Grundlegende Informationen', 'open-data-wizard' ),
array(
Field::make( 'text', 'odw_publisher', __( 'Wer gibt diese Daten heraus?', 'open-data-wizard' ) )
->set_required( true )
->set_default_value( class_exists( 'ODW_Settings' ) ? (string) ODW_Settings::get( 'default_publisher' ) : '' )
->set_attribute( 'placeholder', __( 'z.B. Musterorganisation e.V.', 'open-data-wizard' ) )
->set_help_text( __( 'HERAUSGEBENDE ORGANISATION (dct:publisher)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Musterstadt Statistikamt, Umweltbundesamt, Verbraucherzentrale e.V.', 'open-data-wizard' ) ),
Field::make( 'select', 'odw_theme', __( 'In welche Kategorie gehört dieser Datensatz?', 'open-data-wizard' ) )
->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' ) ),
Field::make( 'text', 'odw_cessda_topic', __( 'Ordnen Sie den Datensatz einem oder mehreren Themenfeld nach CESSDA-Vokabular zu', 'open-data-wizard' ) )
->set_attribute( 'data-odw-backing', 'cessda' )
->set_help_text( __( 'CESSDA THEMENKLASSIFIKATION (dct:subject)', 'open-data-wizard' ) . "\n\n" . __( 'Aus dem CESSDA Controlled Vocabulary (Version 4.2.3, Deutsch). Beispiel: Volkszählungen, Migration, Wirtschaftspolitik', 'open-data-wizard' ) ),
Field::make( 'textarea', 'odw_description', __( 'Worum geht es in diesem Datensatz?', 'open-data-wizard' ) )
->set_required( true )
->set_rows( 5 )
->set_attribute( 'placeholder', __( 'Kurze Beschreibung des Datensatzes…', 'open-data-wizard' ) )
->set_help_text( __( 'BESCHREIBUNG (dct:description)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Ein Überblick über die bevölkerungsreichsten Städte in Deutschland mit statistischen Daten zu Einwohnerzahl und Entwicklung.', '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 Stichworten finde ich diese Daten?', 'open-data-wizard' ) )
->set_rows( 3 )
->set_attribute( 'placeholder', __( 'z.B. Umwelt', 'open-data-wizard' ) )
->set_help_text( __( 'SCHLAGWORTE (dcat:keyword)', 'open-data-wizard' ) . "\n\n" . __( 'Jedes Schlagwort in einer eigenen Zeile. Beispiel: Umwelt, Wasser, Luftverschmutzung', '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' ) ),
Field::make( 'date', 'odw_modified', __( 'Wann wurden diese Daten zuletzt aktualisiert?', 'open-data-wizard' ) )
->set_storage_format( 'Y-m-d' )
->set_picker_options(
array(
'dateFormat' => 'Y-m-d',
'locale' => 'de',
)
)
->set_help_text( __( 'ÄNDERUNGSDATUM (dct:modified)', 'open-data-wizard' ) . "\n\n" . __( 'Wird automatisch bei jeder Speicherung aktualisiert. Beispiel: 2026-04-22', 'open-data-wizard' ) ),
)
)
// -----------------------------------------------------------------
// Tab 3 — Datenbereitstellung (Lizenz + Distribution)
// -----------------------------------------------------------------
->add_tab(
__( '3 — Datenbereitstellung', 'open-data-wizard' ),
array(
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' ) ),
Field::make( 'select', 'odw_format', __( 'In welchem Format ist die Datei?', 'open-data-wizard' ) )
->add_options( self::get_format_options() )
->set_help_text( __( 'FORMAT (dct:format)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: CSV, JSON, PDF', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_byte_size', __( 'Dateigröße (Bytes)', 'open-data-wizard' ) )
->set_attribute( 'type', 'number' )
->set_attribute( 'min', '0' )
->set_attribute( 'data-odw-backing', 'byte_size' ),
Field::make( 'select', 'odw_license', __( 'Unter welcher Lizenz sind diese Daten verfügbar?', 'open-data-wizard' ) )
->set_required( true )
->set_default_value( class_exists( 'ODW_Settings' ) ? (string) ODW_Settings::get( 'default_license' ) : '' )
->add_options( self::get_license_options() )
->set_help_text( __( 'LIZENZ (dct:license)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: CC0 1.0, CC-BY 4.0 – Diese bestimmt, wie andere die Daten nutzen dürfen.', 'open-data-wizard' ) ),
Field::make( 'html', 'odw_license_info' )
->set_html( '<div class="odw-license-info" data-odw-license-info hidden></div>' ),
Field::make( 'text', 'odw_license_custom', __( 'Lizenz-URI eingeben oder auswählen', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'https://example.org/meine-lizenz', 'open-data-wizard' ) )
->set_attribute( 'data-odw-autosuggest', 'license_custom' )
->set_help_text( __( 'EIGENE LIZENZ-URI', 'open-data-wizard' ) . "\n\n" . __( 'Vollständige URI der Lizenz eingeben oder aus der Liste auswählen. Beispiel: https://creativecommons.org/licenses/by/4.0/', 'open-data-wizard' ) )
->set_conditional_logic(
array(
array(
'field' => 'odw_license',
'value' => 'sonstige',
'compare' => '=',
),
)
),
Field::make( 'text', 'odw_attribution_text', __( 'Welcher Namensnennungstext soll bei Weiternutzung angegeben werden?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'optional – nur bei CC BY oder CC BY-SA', 'open-data-wizard' ) )
->set_help_text( __( 'NAMENSNENNUNGSTEXT (dcatde:licenseAttributionByText)', 'open-data-wizard' ) . "\n\n" . __( 'Empfohlen bei CC BY und CC BY-SA Lizenzen. Beispiel: Datensatz von Musterorganisation e.V., bereitgestellt unter CC BY 4.0', 'open-data-wizard' ) )
->set_conditional_logic(
array(
'relation' => 'OR',
array(
'field' => 'odw_license',
'value' => 'https://creativecommons.org/licenses/by/4.0/',
'compare' => '=',
),
array(
'field' => 'odw_license',
'value' => 'https://creativecommons.org/licenses/by-sa/4.0/',
'compare' => '=',
),
)
),
Field::make( 'select', 'odw_availability', __( 'Wie dauerhaft ist diese Datei verfügbar?', 'open-data-wizard' ) )
->add_options( self::get_availability_options() )
->set_help_text( __( 'PLANBARE VERFÜGBARKEIT (dcatap:availability)', 'open-data-wizard' ) . "\n\n" . __( 'Wie verlässlich/dauerhaft ist der Zugriff auf diese Datei geplant? Beispiel: Stabil, Verfügbar, Temporär', 'open-data-wizard' ) ),
)
)
// -----------------------------------------------------------------
// Tab 4 — Erweiterte Angaben (unverändert)
// -----------------------------------------------------------------
->add_tab(
__( '4 — Erweiterte Angaben', 'open-data-wizard' ),
array(
Field::make( 'html', 'odw_ext_hint_landing' )
->set_html( '<h4 style="margin:0 0 4px">' . esc_html__( 'Projektseite & Aktualität', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'text', 'odw_landing_page', __( 'Wo finde ich mehr Informationen zu diesem Projekt?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->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' ) ),
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' ) ),
Field::make( 'html', 'odw_ext_hint_coverage' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'Abdeckung', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'select', 'odw_political_geocoding_level', __( 'Auf welcher Verwaltungsebene wurden diese Daten erhoben?', 'open-data-wizard' ) )
->add_options( self::get_political_geocoding_level_options() )
->set_help_text( __( 'VERWALTUNGSEBENE (dcatde:politicalGeocodingLevelURI)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Gemeinde, Landkreis, Land, Bund', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_political_geocoding_uri', __( 'Auf welches amtliche Gebiet beziehen sich die Daten?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->set_attribute( 'placeholder', 'http://dcat-ap.de/def/politicalGeocoding/regionalKey/09' )
->set_help_text( __( 'AMTLICHER GEBIETSSCHLÜSSEL (dcatde:politicalGeocodingURI)', 'open-data-wizard' ) . "\n\n" . __( 'URI des amtlichen Regional-/Gemeindeschlüssels (AGS/ARS). Beispiel: http://dcat-ap.de/def/politicalGeocoding/regionalKey/09', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_spatial', __( 'Welche geografische Region betreffen diese Daten?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'Region aus der Liste wählen oder eintippen…', 'open-data-wizard' ) )
->set_attribute( 'data-odw-autosuggest', 'spatial' )
->set_help_text( __( 'GEOGRAPHISCHE ABDECKUNG (dct:spatial)', 'open-data-wizard' ) . "\n\n" . __( 'Region aus der Liste wählen (mit GeoNames verknüpft) oder Freitext/URI eingeben. Beispiel: Deutschland, Bayern, Berlin', 'open-data-wizard' ) ),
Field::make( 'date', 'odw_temporal_start', __( 'Ab wann sind diese Daten gültig?', 'open-data-wizard' ) )
->set_storage_format( 'Y-m-d' )
->set_picker_options(
array(
'dateFormat' => 'Y-m-d',
'locale' => 'de',
)
)
->set_help_text( __( 'ZEITLICHER BEZUG — START (dct:temporal)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: 2024-01-01', 'open-data-wizard' ) ),
Field::make( 'date', 'odw_temporal_end', __( 'Bis wann sind diese Daten gültig?', 'open-data-wizard' ) )
->set_storage_format( 'Y-m-d' )
->set_picker_options(
array(
'dateFormat' => 'Y-m-d',
'locale' => 'de',
)
)
->set_help_text( __( 'ZEITLICHER BEZUG — ENDE (dct:temporal)', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: 2024-12-31', 'open-data-wizard' ) ),
Field::make( 'html', 'odw_ext_hint_contact' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'Kontakt', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'text', 'odw_contact_name', __( 'Wer ist Ansprechperson für Fragen zu diesen Daten?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z.B. Open Data Team', 'open-data-wizard' ) )
->set_help_text( __( 'Name oder Organisation der Ansprechperson.', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: Open Data Team, Statistisches Landesamt', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_contact_email', __( 'Unter welcher E-Mail-Adresse kann ich Fragen stellen?', 'open-data-wizard' ) )
->set_attribute( 'type', 'email' )
->set_attribute( 'placeholder', 'opendata@beispiel.de' )
->set_help_text( __( 'E-Mail-Adresse für Rückfragen.', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: opendata@beispiel.de', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_contact_url', __( 'Auf welcher Website finde ich weitere Kontaktinformationen?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->set_attribute( 'placeholder', 'https://beispiel.de/kontakt' )
->set_help_text( __( 'Website mit weiteren Kontaktinformationen.', 'open-data-wizard' ) . "\n\n" . __( 'Beispiel: https://beispiel.de/kontakt', 'open-data-wizard' ) ),
// Toggle: collapses all following Tab-4 fields into an opt-in
// "advanced/pro" section (progressive enhancement via odw-admin-fields.js).
Field::make( 'html', 'odw_pro_toggle' )
->set_html(
'<button type="button" class="odw-pro-toggle" data-odw-pro-toggle aria-expanded="false">'
. '<span class="odw-pro-caret" aria-hidden="true">▸</span> '
. esc_html__( 'Erweiterte Angaben (DCAT-AP.de, HVD, Zugriffsrechte) anzeigen', 'open-data-wizard' )
. '</button>'
),
Field::make( 'html', 'odw_ext_hint_responsibility' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'Verantwortlichkeiten & Herkunft', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'text', 'odw_contributor_id', __( 'Welche Stelle stellt diese Daten im GovData-Verbund bereit?', 'open-data-wizard' ) )
->set_attribute( 'data-odw-vocab', 'contributors' )
->set_attribute( 'placeholder', __( 'Stelle eintippen oder auswählen…', 'open-data-wizard' ) )
->set_help_text( __( 'CONTRIBUTOR-ID (dcatde:contributorID)', 'open-data-wizard' ) . "\n\n" . __( 'Offizielle Kennung der bereitstellenden Stelle aus dem DCAT-AP.de-Verzeichnis. Stelle aus der Liste wählen; die zugehörige offizielle URI wird automatisch verwendet.', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_originator_name', __( 'Wer hat diese Daten ursprünglich erstellt?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z.B. Statistisches Landesamt', 'open-data-wizard' ) )
->set_help_text( __( 'URHEBER (dcatde:originator)', 'open-data-wizard' ) . "\n\n" . __( 'Stelle, von der die Daten ursprünglich stammen (kann von Herausgeber abweichen).', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_originator_email', __( 'E-Mail des Urhebers', 'open-data-wizard' ) )
->set_attribute( 'type', 'email' )
->set_attribute( 'placeholder', 'kontakt@beispiel.de' )
->set_help_text( __( 'E-Mail-Adresse des Urhebers (optional).', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_maintainer_name', __( 'Wer pflegt diese Daten laufend?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z.B. Open Data Team', 'open-data-wizard' ) )
->set_help_text( __( 'PFLEGENDE STELLE (dcatde:maintainer)', 'open-data-wizard' ) . "\n\n" . __( 'Stelle, die für die laufende Pflege/Aktualisierung der Daten zuständig ist.', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_maintainer_email', __( 'E-Mail der pflegenden Stelle', 'open-data-wizard' ) )
->set_attribute( 'type', 'email' )
->set_attribute( 'placeholder', 'opendata@beispiel.de' )
->set_help_text( __( 'E-Mail-Adresse der pflegenden Stelle (optional).', 'open-data-wizard' ) ),
Field::make( 'textarea', 'odw_legal_basis', __( 'Auf welcher rechtlichen Grundlage werden die Daten bereitgestellt?', 'open-data-wizard' ) )
->set_rows( 2 )
->set_attribute( 'placeholder', __( 'z.B. § 12a EGovG, Informationsweiterverwendungsgesetz (IWG)', 'open-data-wizard' ) )
->set_help_text( __( 'RECHTSGRUNDLAGE (dcatde:legalBasis)', 'open-data-wizard' ) . "\n\n" . __( 'Gesetz/Verordnung, das die Bereitstellung regelt. Beispiel: § 12a EGovG', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_quality_process_uri', __( 'Wo ist das Qualitätssicherungs-Verfahren dokumentiert?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->set_attribute( 'placeholder', 'https://beispiel.de/qualitaetssicherung' )
->set_help_text( __( 'QUALITÄTSPROZESS (dcatde:qualityProcessURI)', 'open-data-wizard' ) . "\n\n" . __( 'URL zur Beschreibung des Qualitätssicherungs-Prozesses (optional).', 'open-data-wizard' ) ),
Field::make( 'html', 'odw_ext_hint_access' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'Zugriff & weitere Auffindbarkeit', 'open-data-wizard' ) . '</h4>' ),
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' ) ),
Field::make( 'text', 'odw_theme_uri', __( 'Weiteres EU-Thema (Kategorie-URI)?', 'open-data-wizard' ) )
->set_attribute( 'data-odw-vocab', 'data-theme' )
->set_attribute( 'placeholder', __( 'EU-Datenthema eintippen oder auswählen…', 'open-data-wizard' ) )
->set_help_text( __( 'ZUSÄTZLICHES THEMA (dcat:theme)', 'open-data-wizard' ) . "\n\n" . __( 'Optional ein weiteres Thema aus der offiziellen EU-Themenliste (ergänzt die Kategorie aus Tab 1).', 'open-data-wizard' ) ),
Field::make( 'html', 'odw_ext_hint_hvd' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'High-Value-Datensatz (HVD)', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'select', 'odw_is_hvd', __( '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' ) ),
Field::make( 'select', 'odw_hvd_category', __( 'Welcher HVD-Kategorie gehört dieser Datensatz an?', 'open-data-wizard' ) )
->add_options( self::get_hvd_category_options() )
->set_help_text( __( 'HVD-KATEGORIE (dcatap:hvdCategory)', 'open-data-wizard' ) . "\n\n" . __( 'Eine der sechs EU-Themenkategorien. Beispiel: Georaum, Meteorologie, Mobilität', 'open-data-wizard' ) )
->set_conditional_logic(
array(
array(
'field' => 'odw_is_hvd',
'value' => 'yes',
'compare' => '=',
),
)
),
Field::make( 'html', 'odw_ext_hint_more' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'Weitere DCAT-AP-Felder (optional)', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'text', 'odw_identifier', __( 'Welche eindeutige Kennung hat dieser Datensatz?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z. B. DOI oder interne ID', 'open-data-wizard' ) )
->set_help_text( __( 'IDENTIFIER (dct:identifier)', 'open-data-wizard' ) . "\n\n" . __( 'Eine eindeutige Kennung des Datensatzes (z. B. DOI oder interne ID). Beispiel: 10.1234/abcd', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_type', __( 'Um welchen Typ von Datensatz handelt es sich?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->set_attribute( 'placeholder', 'http://publications.europa.eu/resource/authority/dataset-type/...' )
->set_help_text( __( 'DATENSATZ-TYP (dct:type)', 'open-data-wizard' ) . "\n\n" . __( 'URI aus der EU-Liste „dataset-type". Beispiel: http://publications.europa.eu/resource/authority/dataset-type/STATISTICAL', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_creator_name', __( 'Wer hat diese Daten erstellt?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z. B. Statistisches Amt', 'open-data-wizard' ) )
->set_help_text( __( 'ERSTELLER (dct:creator)', 'open-data-wizard' ) . "\n\n" . __( 'Stelle oder Person, die die Daten erstellt hat (kann vom Herausgeber abweichen).', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_creator_email', __( 'E-Mail-Adresse des Erstellers (optional).', 'open-data-wizard' ) )
->set_attribute( 'type', 'email' )
->set_help_text( __( 'E-MAIL DES ERSTELLERS (foaf:mbox)', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_version', __( 'Welche Version hat dieser Datensatz?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z. B. 1.0', 'open-data-wizard' ) )
->set_help_text( __( 'VERSION (owl:versionInfo)', 'open-data-wizard' ) . "\n\n" . __( 'Versionsbezeichnung des Datensatzes. Beispiel: 1.0, 2024-Q1', 'open-data-wizard' ) ),
Field::make( 'textarea', 'odw_version_notes', __( 'Was hat sich in dieser Version geändert?', 'open-data-wizard' ) )
->set_rows( 2 )
->set_help_text( __( 'VERSIONSHINWEISE (adms:versionNotes)', 'open-data-wizard' ) . "\n\n" . __( 'Kurze Beschreibung der Änderungen gegenüber der Vorversion.', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_spatial_resolution', __( 'Welche räumliche Auflösung haben die Daten (in Metern)?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', __( 'z. B. 30', 'open-data-wizard' ) )
->set_help_text( __( 'RÄUMLICHE AUFLÖSUNG IN METERN (dcat:spatialResolutionInMeters)', 'open-data-wizard' ) . "\n\n" . __( 'Kleinste räumlich auflösbare Distanz in Metern (nur Zahl). Beispiel: 30', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_temporal_resolution', __( 'Welche zeitliche Auflösung haben die Daten?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', 'P1D' )
->set_help_text( __( 'ZEITLICHE AUFLÖSUNG (dcat:temporalResolution)', 'open-data-wizard' ) . "\n\n" . __( 'Als ISO-8601-Dauer. Beispiel: P1D (täglich), PT1H (stündlich)', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_conforms_to', __( 'Welchem Standard oder Schema entsprechen die Daten?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->set_attribute( 'placeholder', 'https://…' )
->set_help_text( __( 'KONFORM ZU (dct:conformsTo)', 'open-data-wizard' ) . "\n\n" . __( 'URI des Standards/Anwendungsprofils, dem die Daten folgen.', 'open-data-wizard' ) ),
Field::make( 'textarea', 'odw_provenance', __( 'Woher stammen die Daten und wie sind sie entstanden?', 'open-data-wizard' ) )
->set_rows( 2 )
->set_help_text( __( 'HERKUNFT (dct:provenance)', 'open-data-wizard' ) . "\n\n" . __( 'Freitext zur Entstehung und Herkunft der Daten.', 'open-data-wizard' ) ),
Field::make( 'html', 'odw_ext_hint_dist' )
->set_html( '<h4 style="margin:16px 0 4px">' . esc_html__( 'Distribution — erweitert', 'open-data-wizard' ) . '</h4>' ),
Field::make( 'text', 'odw_dist_title', __( 'Wie heißt die bereitgestellte Datei/Distribution?', 'open-data-wizard' ) )
->set_help_text( __( 'TITEL DER DISTRIBUTION (dct:title)', 'open-data-wizard' ) . "\n\n" . __( 'Kurzer Titel der Distribution. Beispiel: Gesamtdaten als CSV', 'open-data-wizard' ) ),
Field::make( 'textarea', 'odw_dist_description', __( 'Wie lässt sich die Distribution beschreiben?', 'open-data-wizard' ) )
->set_rows( 2 )
->set_help_text( __( 'BESCHREIBUNG DER DISTRIBUTION (dct:description)', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_download_url', __( 'Direkter Download-Link zur Datei?', 'open-data-wizard' ) )
->set_attribute( 'type', 'url' )
->set_attribute( 'placeholder', 'https://beispiel.de/daten/datei.csv' )
->set_help_text( __( 'DOWNLOAD-URL (dcat:downloadURL)', 'open-data-wizard' ) . "\n\n" . __( 'Direkte URL zum Herunterladen der Datei (im Gegensatz zur Zugriffs-URL).', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_media_type', __( 'Welchen Medientyp (MIME) hat die Datei?', 'open-data-wizard' ) )
->set_attribute( 'placeholder', 'https://www.iana.org/assignments/media-types/text/csv' )
->set_help_text( __( 'MEDIENTYP (dcat:mediaType)', 'open-data-wizard' ) . "\n\n" . __( 'IANA-Media-Type als URI. Beispiel: https://www.iana.org/assignments/media-types/text/csv', 'open-data-wizard' ) ),
Field::make( 'text', 'odw_dist_rights', __( 'Welche Nutzungsrechte gelten für die Datei?', 'open-data-wizard' ) )
->set_help_text( __( 'RECHTE (dct:rights)', 'open-data-wizard' ) . "\n\n" . __( 'URI oder Freitext zu den Nutzungsrechten der Distribution.', 'open-data-wizard' ) ),
)
)
// -----------------------------------------------------------------
// Tab 5 — Vorschau
// -----------------------------------------------------------------
->add_tab(
__( '5 — Vorschau', 'open-data-wizard' ),
array(
Field::make( 'html', 'odw_preview_html' )
->set_html( self::get_preview_html() ),
Field::make( 'html', 'odw_datenatlas_publish' )
->set_html(
'<div class="odw-datenatlas-publish">'
. '<h4 style="margin:16px 0 4px">' . esc_html__( 'Auf dem Datenatlas Zivilgesellschaft veröffentlichen', 'open-data-wizard' ) . '</h4>'
. '<p class="description" style="margin:0 0 10px">' . esc_html__( 'Optional: Machen Sie diesen Datensatz zusätzlich auf dem Datenatlas Zivilgesellschaft auffindbar.', 'open-data-wizard' ) . '</p>'
. '<a href="https://datenatlas-zivilgesellschaft.de" target="_blank" rel="noopener noreferrer" class="button button-primary">'
. esc_html__( 'Zum Datenatlas Zivilgesellschaft', 'open-data-wizard' ) . ' ↗</a>'
. '</div>'
),
)
);
}
/**
* Optional fields are bundled in the tabbed container in register_required_fields().
*/
private static function register_optional_fields(): void {
// Fields are bundled in the tabbed container above.
}
/**
* Distributions are part of the tabbed container in register_required_fields().
*/
private static function register_distributions(): void {
// Distributions are part of the tabbed container above.
}
/**
* Auto-update odw_modified on every save.
*
* @param int $post_id Post ID.
* @param \WP_Post $post Post object.
*/
public static function set_modified_date( int $post_id, \WP_Post $post ): void {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( 'odw_dataset' !== $post->post_type ) {
return;
}
remove_action( 'save_post_odw_dataset', array( self::class, 'set_modified_date' ), 10 );
update_post_meta( $post_id, '_odw_modified', current_time( 'Y-m-d' ) );
add_action( 'save_post_odw_dataset', array( self::class, 'set_modified_date' ), 10, 2 );
}
// -------------------------------------------------------------------------
// Required fields registry — single source of truth for validation
// -------------------------------------------------------------------------
/**
* Returns the required scalar fields definition used by both form rendering
* and the validation class. Each entry: [meta_key, label].
* Loaded from config/dcat-ap-fields.php.
*
* @return array<int, array{meta_key: string, label: string}>
*/
public static function get_required_fields(): array {
$all = self::load_field_definitions();
$required = array();
foreach ( $all as $field ) {
// Only scalar fields (with a meta_key) are validated here.
// Distribution and title are handled separately in ODW_Validation.
if ( $field['required'] && ! empty( $field['meta_key'] ) ) {
$required[] = array(
'meta_key' => $field['meta_key'],
'label' => $field['label'],
);
}
}
return $required;
}
// -------------------------------------------------------------------------
// Config file loaders
// -------------------------------------------------------------------------
/**
* Load field definitions from config/dcat-ap-fields.php.
* Used by ODW_Fields::get_required_fields() and ODW_Quality::get_indicators().
*
* @return array<int, array{key: string, meta_key: string, dcat_prop: string, label: string, points: int, required: bool}>
*/
public static function load_field_definitions(): array {
$file = ODW_PLUGIN_DIR . 'config/dcat-ap-fields.php';
if ( ! file_exists( $file ) ) {
return array();
}
$data = include $file;
return is_array( $data ) ? $data : array();
}
/**
* Load format definitions from config/dct-format-list.php.
*
* @return array<string, array{mime: string, eu_uri: string}>
*/
private static function load_format_list(): array {
$file = ODW_PLUGIN_DIR . 'config/dct-format-list.php';
if ( ! file_exists( $file ) ) {
return array();
}
$data = include $file;
return is_array( $data ) ? $data : array();
}
/**
* Load license options from config/licenses.txt.
* Format: URI | Label (one per line; lines starting with # are comments).
*
* @return array<string, string> URI => Label map (excludes the Sonstige entry).
*/
public static function load_license_list(): array {
$file = ODW_PLUGIN_DIR . 'config/licenses.txt';
if ( ! file_exists( $file ) ) {
return array();
}
$lines = file( $file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
$options = array();
if ( ! is_array( $lines ) ) {
return array();
}
foreach ( $lines as $line ) {
$line = trim( $line );
if ( '' === $line || str_starts_with( $line, '#' ) ) {
continue;
}
$parts = array_map( 'trim', explode( '|', $line, 2 ) );
if ( 2 === count( $parts ) && '' !== $parts[0] && '' !== $parts[1] ) {
$options[ $parts[0] ] = $parts[1];
}
}
return $options;
}
/**
* Parse CESSDA SKOS/RDF file and return concept URI => German label map.
* Result is cached in a transient (24h TTL).
*
* @return array<string, string> URI => German label.
*/
public static function load_cessda_options(): array {
$cache_key = 'odw_cessda_options';
$cached = get_transient( $cache_key );
if ( is_array( $cached ) ) {
return $cached;
}
$file = ODW_PLUGIN_DIR . 'config/TopicClassification-4.2.3_de-4.2.3.rdf';
if ( ! file_exists( $file ) ) {
return array();
}
$options = array();
// Suppress XML warnings; file is valid RDF/XML from CESSDA.
libxml_use_internal_errors( true );
$xml = simplexml_load_file( $file );
libxml_clear_errors();
if ( false === $xml ) {
return array();
}
$xml->registerXPathNamespace( 'rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' );
$xml->registerXPathNamespace( 'skos', 'http://www.w3.org/2004/02/skos/core#' );
$descriptions = $xml->xpath( '//rdf:Description' );
if ( ! is_array( $descriptions ) ) {
return array();
}
foreach ( $descriptions as $desc ) {
$attrs = $desc->attributes( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' );
$uri = (string) ( $attrs['about'] ?? '' );
// Skip ConceptScheme root and entries without a #fragment identifier.
if ( '' === $uri || ! str_contains( $uri, '#' ) ) {
continue;
}
// Get German prefLabel.
$labels = $desc->children( 'http://www.w3.org/2004/02/skos/core#' );
foreach ( $labels as $name => $node ) {
if ( 'prefLabel' !== $name ) {
continue;
}
$xml_attrs = $node->attributes( 'http://www.w3.org/XML/1998/namespace' );
if ( 'de' === (string) ( $xml_attrs['lang'] ?? '' ) ) {
$options[ $uri ] = (string) $node;
break;
}
}
}
// Sort by label for a better auto-suggest experience.
asort( $options );
set_transient( $cache_key, $options, DAY_IN_SECONDS );
return $options;
}
// -------------------------------------------------------------------------
// Controlled vocabulary options
// -------------------------------------------------------------------------
/**
* Lizenzen als URI → Label Map für Select-Felder und den `odw_license_options`-Filter.
* Lädt Standard-Optionen aus dem Formular; vollständige Liste via licenses.txt.
*
* @return array<string, string> Erweiterbar via `add_filter('odw_license_options', ...)`.
*/
public static function get_license_options(): array {
$options = array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
'https://creativecommons.org/publicdomain/zero/1.0/' => __( 'CC0 1.0 — Gemeinfreiheit (keine Rechte vorbehalten)', 'open-data-wizard' ),
'https://creativecommons.org/licenses/by/4.0/' => __( 'CC BY 4.0 — Namensnennung', 'open-data-wizard' ),
'https://creativecommons.org/licenses/by-sa/4.0/' => __( 'CC BY-SA 4.0 — Namensnennung, Weitergabe unter gleichen Bedingungen', 'open-data-wizard' ),
'sonstige' => __( 'Sonstige Lizenz…', 'open-data-wizard' ),
);
return (array) apply_filters( 'odw_license_options', $options );
}
/**
* Kurze, allgemeinverständliche Erklärung je Lizenz (URI → Beschreibungstext).
* Wird unter dem Lizenz-Auswahlfeld angezeigt, sobald eine Lizenz gewählt ist.
*
* @return array<string, string> URI => Beschreibung.
*/
public static function get_license_descriptions(): array {
$descriptions = array(
'https://creativecommons.org/publicdomain/zero/1.0/' => __( 'Die Daten sind gemeinfrei. Sie dürfen ohne Einschränkungen und ohne Namensnennung kopiert, verändert und – auch kommerziell – weiterverwendet werden.', 'open-data-wizard' ),
'https://creativecommons.org/licenses/by/4.0/' => __( 'Die Daten dürfen frei – auch kommerziell – genutzt, verändert und weitergegeben werden, sofern die Quelle (Namensnennung) angegeben wird.', 'open-data-wizard' ),
'https://creativecommons.org/licenses/by-sa/4.0/' => __( 'Wie CC BY: Nutzung und Bearbeitung erlaubt mit Namensnennung. Zusätzlich müssen bearbeitete Daten unter derselben Lizenz weitergegeben werden.', 'open-data-wizard' ),
);
return (array) apply_filters( 'odw_license_descriptions', $descriptions );
}
/**
* Kuratierte Liste geografischer Regionen (Name → GeoNames-URI) für das
* dct:spatial-Feld. Deckt Deutschland, die 16 Bundesländer und größere Städte ab.
* Erweiterbar via `odw_spatial_options`-Filter.
*
* @return array<string, string> Regionsname => GeoNames-URI.
*/
public static function get_spatial_options(): array {
$base = 'https://sws.geonames.org/';
$options = array(
'Deutschland' => $base . '2921044/',
'Baden-Württemberg' => $base . '2953481/',
'Bayern' => $base . '2951839/',
'Berlin' => $base . '2950157/',
'Brandenburg' => $base . '2945356/',
'Bremen' => $base . '2944387/',
'Hamburg' => $base . '2911297/',
'Hessen' => $base . '2905330/',
'Mecklenburg-Vorpommern' => $base . '2872567/',
'Niedersachsen' => $base . '2862926/',
'Nordrhein-Westfalen' => $base . '2861876/',
'Rheinland-Pfalz' => $base . '2847618/',
'Saarland' => $base . '2842635/',
'Sachsen' => $base . '2842566/',
'Sachsen-Anhalt' => $base . '2842565/',
'Schleswig-Holstein' => $base . '2838632/',
'Thüringen' => $base . '2822542/',
'München' => $base . '2867714/',
'Köln' => $base . '2886242/',
'Frankfurt am Main' => $base . '2925533/',
'Stuttgart' => $base . '2825297/',
'Düsseldorf' => $base . '2934246/',
'Leipzig' => $base . '2879139/',
'Dresden' => $base . '2935022/',
'Hannover' => $base . '2910831/',
'Nürnberg' => $base . '2861650/',
);
return (array) apply_filters( 'odw_spatial_options', $options );
}
/**
* Translate a license URI to its human-readable label.
* Checks known options first, then the external licenses.txt list.
*
* @param string $uri License URI (or 'sonstige').
* @return string Human-readable label, or the URI itself if not found.
*/
public static function get_license_label( string $uri ): string {
$options = self::get_license_options();
if ( isset( $options[ $uri ] ) ) {
return $options[ $uri ];
}
$extended = self::load_license_list();
return $extended[ $uri ] ?? $uri;
}
/**
* Themen-Vokabular als EU-Vocabulary-URI → Label Map.
*
* @return array<string, string>
*/
public static function get_theme_options(): array {
$base = 'http://publications.europa.eu/resource/authority/data-theme/';
$options = array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
$base . 'EDUC' => __( 'Bildung, Kultur & Sport', 'open-data-wizard' ),
$base . 'HEAL' => __( 'Gesundheit', 'open-data-wizard' ),
$base . 'SOCI' => __( 'Bevölkerung & Gesellschaft', 'open-data-wizard' ),
$base . 'ENVI' => __( 'Umwelt', 'open-data-wizard' ),
$base . 'ECON' => __( 'Wirtschaft & Finanzen', 'open-data-wizard' ),
$base . 'GOVE' => __( 'Verwaltung & öffentlicher Sektor', 'open-data-wizard' ),
$base . 'TECH' => __( 'Wissenschaft & Technologie', 'open-data-wizard' ),
$base . 'TRAN' => __( 'Verkehr', 'open-data-wizard' ),
$base . 'AGRI' => __( 'Landwirtschaft & Ernährung', 'open-data-wizard' ),
$base . 'ENER' => __( 'Energie', 'open-data-wizard' ),
$base . 'JUST' => __( 'Justiz & Sicherheit', 'open-data-wizard' ),
$base . 'REGI' => __( 'Regionen & Städte', 'open-data-wizard' ),
$base . 'INTR' => __( 'Internationale Themen', 'open-data-wizard' ),
);
return (array) apply_filters( 'odw_theme_options', $options );
}
/**
* Aktualisierungsfrequenzen.
*
* @return array<string, string>
*/
public static function get_periodicity_options(): array {
$base = 'http://publications.europa.eu/resource/authority/frequency/';
return array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
$base . 'DAILY' => __( 'Täglich', 'open-data-wizard' ),
$base . 'WEEKLY' => __( 'Wöchentlich', 'open-data-wizard' ),
$base . 'MONTHLY' => __( 'Monatlich', 'open-data-wizard' ),
$base . 'QUARTERLY' => __( 'Vierteljährlich', 'open-data-wizard' ),
$base . 'ANNUAL' => __( 'Jährlich', 'open-data-wizard' ),
$base . 'BIENNIAL' => __( 'Zweijährlich', 'open-data-wizard' ),
$base . 'IRREG' => __( 'Unregelmäßig', 'open-data-wizard' ),
$base . 'UNKNOWN' => __( 'Unbekannt', 'open-data-wizard' ),
);
}
/**
* Dateiformate aus config/dct-format-list.php.
*
* @return array<string, string>
*/
public static function get_format_options(): array {
$list = self::load_format_list();
$options = array( '' => __( '— Bitte wählen —', 'open-data-wizard' ) );
foreach ( array_keys( $list ) as $key ) {
$options[ $key ] = $key;
}
return $options;
}
/**
* Format MIME-type mapping — loaded from config/dct-format-list.php.
*
* @param string $format Short format label (e.g. "CSV").
* @return string MIME type, or the original format string if unknown.
*/
public static function get_format_mime( string $format ): string {
$list = self::load_format_list();
return $list[ $format ]['mime'] ?? $format;
}
/**
* Returns the config entry for a format label (mime, eu_uri and MQA flags).
*
* @param string $format Short format label (e.g. "CSV").
* @return array{mime?: string, eu_uri?: string, machine_readable?: bool, non_proprietary?: bool}
*/
public static function get_format_meta( string $format ): array {
$list = self::load_format_list();
return $list[ $format ] ?? array();
}
/**
* Maps a short format label to its EU Publications Office file-type URI.
*
* @param string $format Short format label (e.g. "CSV").
* @return string EU file-type URI, or the original string if unknown.
*/
public static function get_format_eu_uri( string $format ): string {
$list = self::load_format_list();
$eu_code = $list[ $format ]['eu_uri'] ?? '';
if ( '' === $eu_code ) {
return $format;
}
return 'http://publications.europa.eu/resource/authority/file-type/' . $eu_code;
}
/**
* Language options.
*
* @return array<string, string>
*/
public static function get_language_options(): array {
$base = 'http://publications.europa.eu/resource/authority/language/';
return array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
$base . 'DEU' => __( 'Deutsch (DE)', 'open-data-wizard' ),
$base . 'ENG' => __( 'Englisch (EN)', 'open-data-wizard' ),
);
}
/**
* Administrative geocoding level options.
*
* @return array<string, string>
*/
public static function get_political_geocoding_level_options(): array {
$base = 'http://dcat-ap.de/def/politicalGeocoding/Level/';
return array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
$base . 'federal' => __( 'Bund (Federal)', 'open-data-wizard' ),
$base . 'state' => __( 'Land (Bundesland)', 'open-data-wizard' ),
$base . 'administrativeDistrict' => __( 'Landkreis', 'open-data-wizard' ),
$base . 'municipality' => __( 'Gemeinde', 'open-data-wizard' ),
);
}
/**
* High-Value-Dataset (HVD) Ja/Nein options.
*
* @return array<string, string>
*/
public static function get_hvd_flag_options(): array {
return array(
'' => __( 'Nein', 'open-data-wizard' ),
'yes' => __( 'Ja, dieser Datensatz ist ein High-Value-Datensatz', 'open-data-wizard' ),
);
}
/**
* High-Value-Dataset thematic categories (EU Implementing Regulation 2023/138).
* Values are the canonical concept URIs from the EU "High-value dataset
* categories" authority table (http://data.europa.eu/bna/).
*
* @return array<string, string>
*/
public static function get_hvd_category_options(): array {
$base = 'http://data.europa.eu/bna/';
return array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
$base . 'c_ac64a52d' => __( 'Georaum (Geospatial)', 'open-data-wizard' ),
$base . 'c_dd313021' => __( 'Erdbeobachtung und Umwelt', 'open-data-wizard' ),
$base . 'c_164e0bf5' => __( 'Meteorologie', 'open-data-wizard' ),
$base . 'c_e1da4e07' => __( 'Statistik', 'open-data-wizard' ),
$base . 'c_a9135398' => __( 'Unternehmen und Eigentümerschaft von Unternehmen', 'open-data-wizard' ),
$base . 'c_b79e35eb' => __( 'Mobilität', 'open-data-wizard' ),
);
}
/**
* Planned-availability options (DCAT-AP.de `dcatap:availability`).
* Values are the canonical concept URIs from the DCAT-AP.de planned
* availability vocabulary (http://dcat-ap.de/def/plannedAvailability/).
*
* @return array<string, string>
*/
public static function get_availability_options(): array {
// EU-managed authority table (required by the EU dcatap:availability SHACL);
// the deprecated dcat-ap.de/def/plannedAvailability list must not be used.
$base = 'http://publications.europa.eu/resource/authority/planned-availability/';
return array(
'' => __( '— Bitte wählen —', 'open-data-wizard' ),
$base . 'AVAILABLE' => __( 'Verfügbar (mittelfristige Planung)', 'open-data-wizard' ),
$base . 'STABLE' => __( 'Stabil (langfristig verfügbar)', 'open-data-wizard' ),
$base . 'EXPERIMENTAL' => __( 'Experimentell', 'open-data-wizard' ),
$base . 'TEMPORARY' => __( 'Temporär (kann jederzeit entfallen)', 'open-data-wizard' ),
);
}
/**
* Access-rights options (DCAT-AP `dct:accessRights`), sourced from the bundled
* EU access-right vocabulary (config/vocabularies/access-right.json).
*
* @return array<string, string>
*/
public static function get_access_rights_options(): array {
$options = array( '' => __( '— Bitte wählen —', 'open-data-wizard' ) );
foreach ( self::load_vocabulary( 'access-right' ) as $entry ) {
$options[ $entry['value'] ] = $entry['label'];
}
return $options;
}
/**
* Loads a bundled controlled vocabulary from config/vocabularies/<id>.json.
* Each entry is normalised to { value, label } for the autosuggest widget.
* Results are cached in a transient for 24h.
*
* @param string $id Vocabulary identifier (e.g. 'contributors').
* @return array<int, array{value: string, label: string}>
*/
public static function load_vocabulary( string $id ): array {
$id = preg_replace( '/[^a-z0-9_-]/', '', $id );
if ( '' === (string) $id ) {
return array();
}
$cache_key = 'odw_vocab_' . $id;
$cached = get_transient( $cache_key );
if ( is_array( $cached ) ) {
return $cached;
}
$file = ODW_PLUGIN_DIR . 'config/vocabularies/' . $id . '.json';
if ( ! is_readable( $file ) ) {
return array();
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local bundled config file, not a remote URL.
$decoded = json_decode( (string) file_get_contents( $file ), true );
if ( ! is_array( $decoded ) ) {
return array();
}
$out = array();
foreach ( $decoded as $entry ) {
if ( ! is_array( $entry ) || empty( $entry['value'] ) ) {
continue;
}
$out[] = array(
'value' => (string) $entry['value'],
'label' => (string) ( $entry['label'] ?? $entry['value'] ),
);
}
set_transient( $cache_key, $out, DAY_IN_SECONDS );
return $out;
}
// -------------------------------------------------------------------------
// HTML helpers
// -------------------------------------------------------------------------
/**
* Field definitions for the live wizard preview (Tab 5).
*
* Single source of truth shared by the preview markup (get_preview_html())
* and the JS that fills it (localised in ODW_Admin::enqueue_assets()). Each
* entry maps a form field to a human-readable label and flags whether the
* field is a publishing requirement (shown in the completeness checklist)
* and/or part of the summary card. The "key" matches the Carbon Fields name
* (without the leading underscore); the special key "title" maps to the
* native post-title input.
*
* @return array<int, array{key: string, label: string, required: bool, card: bool}>
*/
public static function get_live_preview_fields(): array {
return array(