geonetwork-ui 2.10.0-dev.122ccbde0 → 2.10.0-dev.12ab1b88c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1603 -6150
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +251 -177
  4. package/index.d.ts.map +1 -1
  5. package/package.json +25 -35
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +20 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +4 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +23 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +8 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +2 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +2 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +2 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +2 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +4 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +23 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +2 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +4 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +2 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +9 -0
  20. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
  21. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +2 -0
  22. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +6 -5
  23. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  24. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  25. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +11 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +21 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +52 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +24 -1
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +73 -0
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +79 -0
  31. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
  32. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  33. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +66 -40
  34. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  35. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  36. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
  37. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +23 -5
  38. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  39. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  40. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  41. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  42. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -1
  43. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +62 -0
  44. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  45. package/src/libs/feature/editor/src/index.ts +1 -0
  46. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +4 -1
  47. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
  48. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
  49. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  50. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  51. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
  52. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +41 -0
  53. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  54. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  55. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  56. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  57. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
  58. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +6 -7
  59. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +9 -2
  60. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +12 -0
  61. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  62. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.css +0 -0
  63. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.html +5 -0
  64. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.ts +89 -0
  65. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  67. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  68. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  69. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  70. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
  72. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +28 -16
  73. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +16 -0
  74. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  75. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +34 -2
  76. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -6
  77. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +92 -33
  78. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  79. package/src/libs/feature/editor/src/lib/fields.config.ts +354 -337
  80. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -3
  81. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  82. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  83. package/src/libs/feature/notify-reuse/src/index.ts +2 -0
  84. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.css +0 -0
  85. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.html +23 -0
  86. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.ts +114 -0
  87. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  88. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -0
  89. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +234 -0
  90. package/src/libs/feature/notify-reuse/src/lib/utils/url.ts +16 -0
  91. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  92. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  93. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  94. package/src/libs/ui/dataviz/src/index.ts +0 -1
  95. package/src/libs/ui/elements/src/index.ts +2 -0
  96. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +88 -0
  97. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  98. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +29 -4
  99. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +51 -7
  100. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  101. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  102. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  103. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  104. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  105. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  106. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  107. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  108. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  109. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +5 -0
  110. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +7 -10
  111. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +2 -2
  112. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  113. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  114. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  115. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  116. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  117. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  118. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  119. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  120. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
  121. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
  122. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
  123. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  124. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
  125. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  126. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  127. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  128. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  129. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  130. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  131. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
  132. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  133. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  134. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  135. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  136. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  137. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  138. package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
  139. package/src/libs/util/app-config/src/lib/app-config.ts +70 -9
  140. package/src/libs/util/app-config/src/lib/model.ts +7 -0
  141. package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
  142. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  143. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  144. package/src/libs/util/i18n/src/index.ts +0 -1
  145. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  146. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  147. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  148. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  149. package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
  150. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  151. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  152. package/src/libs/util/shared/src/lib/utils/user-display.ts +9 -0
  153. package/style.css +0 -1
  154. package/tailwind.base.css +15 -0
  155. package/translations/de.json +111 -50
  156. package/translations/en.json +116 -55
  157. package/translations/es.json +85 -24
  158. package/translations/fr.json +113 -52
  159. package/translations/it.json +112 -51
  160. package/translations/nl.json +84 -23
  161. package/translations/pt.json +85 -24
  162. package/translations/sk.json +85 -24
  163. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
@@ -12,6 +12,54 @@ import {
12
12
  MouseWheelZoom,
13
13
  } from 'ol/interaction.js'
14
14
  import MapBrowserEvent from 'ol/MapBrowserEvent.js'
15
+ import type { DatasetSpatialExtent } from '../../../../../libs/common/domain/src/lib/model/record'
16
+ import type {
17
+ MapContextLayer,
18
+ MapContextLayerGeojson,
19
+ } from '@geospatial-sdk/core'
20
+ import { spatialExtentsToFeatureCollection } from '../../../../../libs/util/shared/src'
21
+
22
+ export type SpatialExtentLayerStyle = NonNullable<
23
+ MapContextLayerGeojson['style']
24
+ >
25
+
26
+ /**
27
+ * Default style for a spatial-extent layer: a solid black outline over a
28
+ * translucent grey fill.
29
+ */
30
+ export const DEFAULT_SPATIAL_EXTENT_STYLE: SpatialExtentLayerStyle = {
31
+ 'stroke-color': 'black',
32
+ 'stroke-width': 2,
33
+ 'fill-color': 'rgba(153, 153, 153, 0.3)',
34
+ }
35
+
36
+ /**
37
+ * Builds a GeoJSON map layer drawing the given spatial extents: each extent is
38
+ * rendered from its own geometry or, when only a bounding box is available,
39
+ * from a polygon derived from that box.
40
+ *
41
+ * @returns the layer, or `null` when none of the extents can be represented.
42
+ */
43
+ export function createSpatialExtentLayer(
44
+ extents: DatasetSpatialExtent[],
45
+ overrides?: {
46
+ label?: string
47
+ clickable?: boolean
48
+ style?: SpatialExtentLayerStyle
49
+ }
50
+ ): MapContextLayer | null {
51
+ const data = spatialExtentsToFeatureCollection(extents)
52
+ if (data.features.length === 0) {
53
+ return null
54
+ }
55
+ return {
56
+ type: 'geojson',
57
+ data,
58
+ label: 'Spatial extents',
59
+ style: DEFAULT_SPATIAL_EXTENT_STYLE,
60
+ ...overrides,
61
+ }
62
+ }
15
63
 
16
64
  export function prioritizePageScroll(interactions: Collection<Interaction>) {
17
65
  interactions.clear()
@@ -52,7 +52,7 @@ export class RecordPreviewFeedComponent extends RecordPreviewComponent {
52
52
  protected elementRef: ElementRef
53
53
  private translate = inject(TranslateService)
54
54
 
55
- timeFormat = new Duration(this.translate.currentLang, {})
55
+ timeFormat = new Duration(this.translate.getCurrentLang(), {})
56
56
 
57
57
  constructor() {
58
58
  const elementRef = inject(ElementRef)
@@ -52,10 +52,10 @@
52
52
  class="w-72 p-6 flex flex-col gap-3 mt-2 border border-gray-100 bg-white shadow-2xl rounded-2xl"
53
53
  >
54
54
  <span class="text-lg font-bold text-center">{{
55
- 'editor.record.delete.confirmation.title' | translate
55
+ 'editor.record.delete.confirmation.title' | translate: { recordKind }
56
56
  }}</span>
57
57
  <span class="text-center">{{
58
- 'editor.record.delete.confirmation.message' | translate
58
+ 'editor.record.delete.confirmation.message' | translate: { recordKind }
59
59
  }}</span>
60
60
  <div class="flex flex-row gap-8 justify-center">
61
61
  <gn-ui-button
@@ -65,14 +65,16 @@
65
65
  data-cy="confirm-button"
66
66
  [style.--gn-ui-button-width]="'100px'"
67
67
  >{{
68
- 'editor.record.delete.confirmation.confirmText' | translate
68
+ 'editor.record.delete.confirmation.confirmText'
69
+ | translate: { recordKind }
69
70
  }}</gn-ui-button
70
71
  >
71
72
  <gn-ui-button
72
73
  [style.--gn-ui-button-width]="'100px'"
73
74
  (buttonClick)="closeActionMenu.emit()"
74
75
  >{{
75
- 'editor.record.delete.confirmation.cancelText' | translate
76
+ 'editor.record.delete.confirmation.cancelText'
77
+ | translate: { recordKind }
76
78
  }}</gn-ui-button
77
79
  >
78
80
  </div>
@@ -84,10 +86,10 @@
84
86
  class="w-80 p-6 flex flex-col gap-3 mt-2 border border-gray-100 bg-white shadow-2xl rounded-2xl"
85
87
  >
86
88
  <span class="text-lg font-bold text-center">{{
87
- 'editor.record.undo.confirmation.title' | translate
89
+ 'editor.record.undo.confirmation.title' | translate: { recordKind }
88
90
  }}</span>
89
91
  <span class="text-center">{{
90
- 'editor.record.undo.confirmation.message' | translate
92
+ 'editor.record.undo.confirmation.message' | translate: { recordKind }
91
93
  }}</span>
92
94
  <div class="flex flex-row gap-8 justify-center">
93
95
  <gn-ui-button
@@ -97,14 +99,16 @@
97
99
  data-cy="confirm-button"
98
100
  [style.--gn-ui-button-width]="'120px'"
99
101
  >{{
100
- 'editor.record.undo.confirmation.confirmText' | translate
102
+ 'editor.record.undo.confirmation.confirmText'
103
+ | translate: { recordKind }
101
104
  }}</gn-ui-button
102
105
  >
103
106
  <gn-ui-button
104
107
  [style.--gn-ui-button-width]="'120px'"
105
108
  (buttonClick)="closeActionMenu.emit()"
106
109
  >{{
107
- 'editor.record.undo.confirmation.cancelText' | translate
110
+ 'editor.record.undo.confirmation.cancelText'
111
+ | translate: { recordKind }
108
112
  }}</gn-ui-button
109
113
  >
110
114
  </div>
@@ -2,15 +2,16 @@ import {
2
2
  ChangeDetectorRef,
3
3
  Component,
4
4
  EventEmitter,
5
+ inject,
5
6
  Input,
6
7
  Output,
7
8
  ViewChild,
8
- inject,
9
9
  } from '@angular/core'
10
10
  import { MatDialog, MatDialogModule } from '@angular/material/dialog'
11
11
  import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'
12
12
  import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
13
13
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
14
+ import { RecordKind } from '../../../../../../../libs/common/domain/src/lib/model'
14
15
 
15
16
  type ActionMenuPage = 'mainMenu' | 'deleteMenu' | 'rollbackMenu'
16
17
 
@@ -35,6 +36,7 @@ export class ActionMenuComponent {
35
36
  @Input() isDuplicating: boolean
36
37
  @Input() canDelete = true
37
38
  @Input() page: 'draft' | 'main' | 'record'
39
+ @Input() recordKind: RecordKind
38
40
  @Output() duplicate = new EventEmitter<void>()
39
41
  @Output() delete = new EventEmitter<void>()
40
42
  @Output() closeActionMenu = new EventEmitter<void>()
@@ -172,6 +172,7 @@
172
172
  (delete)="handleDelete(item)"
173
173
  (closeActionMenu)="closeActionMenu()"
174
174
  (rollback)="handleRollback(item)"
175
+ [recordKind]="item.kind"
175
176
  >
176
177
  </gn-ui-action-menu>
177
178
  </ng-template>
@@ -28,7 +28,13 @@ export class PopoverComponent implements AfterViewInit, OnChanges, OnDestroy {
28
28
 
29
29
  @ViewChild('popoverContent', { static: false }) popoverContent: ElementRef
30
30
  @Input() content: string | TemplateRef<any>
31
- @Input() theme: 'light' | 'light-border' | 'translucent' | 'material' | ''
31
+ @Input() theme:
32
+ | 'light'
33
+ | 'light-border'
34
+ | 'translucent'
35
+ | 'material'
36
+ | 'dark'
37
+ | ''
32
38
 
33
39
  private tippyInstance: Instance
34
40
  private view: EmbeddedViewRef<any>
@@ -1,6 +1,6 @@
1
1
  <span>
2
2
  <svg
3
- class="animate-spin h-8 w-8 text-primary"
3
+ class="animate-spin"
4
4
  xmlns="http://www.w3.org/2000/svg"
5
5
  fill="none"
6
6
  viewBox="0 0 24 24"
@@ -1,6 +1,8 @@
1
1
  import * as TOML from '@ltd/j-toml'
2
2
  import {
3
3
  checkMetadataLanguage,
4
+ checkNewRecordDefaultLanguage,
5
+ checkNewRecordStandard,
4
6
  parseConfigSection,
5
7
  parseMultiConfigSection,
6
8
  parseTranslationsConfigSection,
@@ -8,6 +10,7 @@ import {
8
10
  import {
9
11
  CustomTranslations,
10
12
  CustomTranslationsAllLanguages,
13
+ EditorConfig,
11
14
  GlobalConfig,
12
15
  LayerConfig,
13
16
  MapConfig,
@@ -15,10 +18,15 @@ import {
15
18
  SearchConfig,
16
19
  ThemeConfig,
17
20
  } from './model'
18
- import { TranslateCompiler, TranslateLoader } from '@ngx-translate/core'
21
+ import {
22
+ TranslateCompiler,
23
+ TranslateLoader,
24
+ TranslateModuleConfig,
25
+ } from '@ngx-translate/core'
19
26
  import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
20
- import { HttpClient } from '@angular/common/http'
21
27
  import { FileWithOverridesTranslateLoader } from './i18n/file-with-overrides.translate.loader'
28
+ import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader'
29
+ import { HttpClient } from '@angular/common/http'
22
30
 
23
31
  const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
24
32
  This error might show up in case of an invalid/malformed configuration file.
@@ -51,6 +59,12 @@ export function getOptionalSearchConfig(): SearchConfig | null {
51
59
  return searchConfig
52
60
  }
53
61
 
62
+ let editorConfig: EditorConfig = null
63
+
64
+ export function getOptionalEditorConfig(): EditorConfig | null {
65
+ return editorConfig
66
+ }
67
+
54
68
  let metadataQualityConfig: MetadataQualityConfig = null
55
69
  export function getMetadataQualityConfig(): MetadataQualityConfig {
56
70
  return (
@@ -285,6 +299,45 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
285
299
  SORTABLE: parsedMetadataQualitySection.sortable,
286
300
  } as MetadataQualityConfig)
287
301
 
302
+ let parsedEditingSection = parseConfigSection(
303
+ parsed,
304
+ 'editing',
305
+ [],
306
+ ['new_record_default_language', 'new_record_standard'],
307
+ warnings,
308
+ errors
309
+ )
310
+ if (
311
+ parsedEditingSection !== null &&
312
+ parsedEditingSection.new_record_default_language !== undefined
313
+ ) {
314
+ parsedEditingSection = checkNewRecordDefaultLanguage(
315
+ parsedEditingSection,
316
+ warnings
317
+ )
318
+ }
319
+ if (
320
+ parsedEditingSection !== null &&
321
+ parsedEditingSection.new_record_standard !== undefined
322
+ ) {
323
+ parsedEditingSection = checkNewRecordStandard(
324
+ parsedEditingSection,
325
+ warnings
326
+ )
327
+ }
328
+ editorConfig =
329
+ parsedEditingSection === null
330
+ ? null
331
+ : ({
332
+ NEW_RECORD_DEFAULT_LANGUAGE:
333
+ parsedEditingSection.new_record_default_language as
334
+ | string
335
+ | undefined,
336
+ NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard as
337
+ | EditorConfig['NEW_RECORD_STANDARD']
338
+ | undefined,
339
+ } as EditorConfig)
340
+
288
341
  customTranslations = parseTranslationsConfigSection(
289
342
  parsed,
290
343
  'translations'
@@ -309,19 +362,27 @@ export function isConfigLoaded() {
309
362
  export function _reset() {
310
363
  globalConfig = null
311
364
  themeConfig = null
365
+ editorConfig = null
312
366
  customTranslations = null
313
367
  }
314
368
 
315
- export const TRANSLATE_WITH_OVERRIDES_CONFIG = {
369
+ export const TRANSLATE_WITH_OVERRIDES_CONFIG: TranslateModuleConfig = {
316
370
  compiler: {
317
371
  provide: TranslateCompiler,
318
372
  useClass: TranslateMessageFormatCompiler,
319
373
  },
320
- loader: {
321
- provide: TranslateLoader,
322
- useFactory: function HttpLoaderFactory(http: HttpClient) {
323
- return new FileWithOverridesTranslateLoader(http, './assets/i18n/')
374
+ loader: [
375
+ {
376
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
377
+ useValue: {
378
+ prefix: './assets/i18n/',
379
+ suffix: '.json',
380
+ },
324
381
  },
325
- deps: [HttpClient],
326
- },
382
+ {
383
+ provide: TranslateLoader,
384
+ useClass: FileWithOverridesTranslateLoader,
385
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
386
+ },
387
+ ],
327
388
  }
@@ -69,6 +69,13 @@ export interface MetadataQualityConfig {
69
69
  ENABLED: boolean
70
70
  }
71
71
 
72
+ export type NewRecordStandard = 'iso19139' | 'iso19115-3'
73
+
74
+ export interface EditorConfig {
75
+ NEW_RECORD_DEFAULT_LANGUAGE?: string
76
+ NEW_RECORD_STANDARD?: NewRecordStandard
77
+ }
78
+
72
79
  export type CustomTranslations = { [translationKey: string]: string }
73
80
  export type CustomTranslationsAllLanguages = {
74
81
  [lang: string]: CustomTranslations
@@ -1,4 +1,4 @@
1
- import { toLang2 } from '../../../../../libs/util/i18n/src'
1
+ import { LANG_2_TO_3_MAPPER, toLang2 } from '../../../../../libs/util/i18n/src'
2
2
 
3
3
  const flatten = (
4
4
  base: string,
@@ -137,3 +137,52 @@ export function checkMetadataLanguage(
137
137
  }
138
138
  return parsedConfigSection
139
139
  }
140
+
141
+ export function checkNewRecordDefaultLanguage(
142
+ parsedConfigSection: any,
143
+ outWarnings: string[]
144
+ ) {
145
+ const lang2 = toLang2(
146
+ parsedConfigSection.new_record_default_language.toLowerCase()
147
+ )
148
+ if (!(lang2 in LANG_2_TO_3_MAPPER)) {
149
+ outWarnings.push(
150
+ `In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`
151
+ )
152
+ return {
153
+ ...parsedConfigSection,
154
+ new_record_default_language: undefined,
155
+ }
156
+ }
157
+ return {
158
+ ...parsedConfigSection,
159
+ new_record_default_language: lang2,
160
+ }
161
+ }
162
+
163
+ export function checkNewRecordStandard(
164
+ parsedConfigSection: any,
165
+ outWarnings: string[]
166
+ ) {
167
+ const standard = parsedConfigSection.new_record_standard
168
+ const normalizedStandard =
169
+ typeof standard === 'string' ? standard.trim().toLowerCase() : null
170
+
171
+ if (
172
+ normalizedStandard === 'iso19139' ||
173
+ normalizedStandard === 'iso19115-3'
174
+ ) {
175
+ return {
176
+ ...parsedConfigSection,
177
+ new_record_standard: normalizedStandard,
178
+ }
179
+ }
180
+
181
+ outWarnings.push(
182
+ `In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`
183
+ )
184
+ return {
185
+ ...parsedConfigSection,
186
+ new_record_standard: undefined,
187
+ }
188
+ }
@@ -1,4 +1,3 @@
1
- import { BaseReader } from './base'
2
1
  import { DataItem, DatasetInfo, PropertyInfo } from '../model'
3
2
  import { getJsonDataItemsProxy, jsonToGeojsonFeature } from '../utils'
4
3
  import { generateSqlQuery } from '../sql-utils'
@@ -58,7 +57,7 @@ export class BaseFileReader extends BaseCacheReader {
58
57
  this.groupedBy,
59
58
  this.aggregations
60
59
  )
61
- const result = await import('alasql').then((module) =>
60
+ const result: any[] = await import('alasql').then((module) =>
62
61
  module.default(query, [jsonItems])
63
62
  )
64
63
  return result.map(jsonToGeojsonFeature)
@@ -1,10 +1,13 @@
1
1
  import { EndpointError, WfsEndpoint, WfsVersion } from '@camptocamp/ogc-client'
2
2
  import { DataItem, DatasetInfo, FetchError, PropertyInfo } from '../model'
3
- import { fetchDataAsText } from '../utils'
3
+ import {
4
+ fetchDataAsText,
5
+ getJsonDataItemsProxy,
6
+ jsonToGeojsonFeature,
7
+ } from '../utils'
4
8
  import { GmlReader, parseGml } from './gml'
5
9
  import { GeojsonReader, parseGeojson } from './geojson'
6
10
  import { BaseCacheReader } from './base-cache'
7
- import { getJsonDataItemsProxy, jsonToGeojsonFeature } from '../utils'
8
11
  import { generateSqlQuery } from '../sql-utils'
9
12
 
10
13
  export async function getWfsEndpoint(wfsUrl: string): Promise<WfsEndpoint> {
@@ -167,7 +170,7 @@ export class WfsReader extends BaseCacheReader {
167
170
  this.groupedBy,
168
171
  this.aggregations
169
172
  )
170
- const result = await import('alasql').then((module) =>
173
+ const result: any[] = await import('alasql').then((module) =>
171
174
  module.default(query, [jsonItems])
172
175
  )
173
176
  return result.map(jsonToGeojsonFeature)
@@ -2,6 +2,5 @@ export * from './lib/i18n.providers'
2
2
  export * from './lib/i18n.constants'
3
3
  export * from './lib/i18n.interceptor'
4
4
  export * from './lib/file.translate.loader'
5
- export * from './lib/embedded.translate.loader'
6
5
  export * from './lib/translate.loader.utils'
7
6
  export * from './lib/language-codes'
@@ -1,6 +1,4 @@
1
- import { HttpClient } from '@angular/common/http'
2
1
  import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
3
- import { FileTranslateLoader } from './file.translate.loader'
4
2
  import {
5
3
  TranslateCompiler,
6
4
  TranslateDefaultParser,
@@ -9,26 +7,34 @@ import {
9
7
  TranslateParser,
10
8
  } from '@ngx-translate/core'
11
9
  import { Injectable } from '@angular/core'
10
+ import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader'
11
+ import { HttpClient } from '@angular/common/http'
12
+ import { FileTranslateLoader } from './file.translate.loader'
12
13
 
13
14
  export const DEFAULT_LANG = 'en'
14
15
 
15
16
  // Caution: changing this can break language selection from third parties!
16
17
  export const LANGUAGE_STORAGE_KEY = 'geonetwork-ui-language'
17
18
 
18
- export function HttpLoaderFactory(http: HttpClient) {
19
- return new FileTranslateLoader(http, './assets/i18n/')
20
- }
21
-
22
19
  export const TRANSLATE_DEFAULT_CONFIG: TranslateModuleConfig = {
23
20
  compiler: {
24
21
  provide: TranslateCompiler,
25
22
  useClass: TranslateMessageFormatCompiler,
26
23
  },
27
- loader: {
28
- provide: TranslateLoader,
29
- useFactory: HttpLoaderFactory,
30
- deps: [HttpClient],
31
- },
24
+ loader: [
25
+ {
26
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
27
+ useValue: {
28
+ prefix: './assets/i18n/',
29
+ suffix: '.json',
30
+ },
31
+ },
32
+ {
33
+ provide: TranslateLoader,
34
+ useClass: FileTranslateLoader,
35
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
36
+ },
37
+ ],
32
38
  }
33
39
 
34
40
  @Injectable()
@@ -6,7 +6,7 @@ import {
6
6
  } from '@angular/common/http'
7
7
  import { DEFAULT_LANG } from './i18n.constants'
8
8
  import { Observable } from 'rxjs'
9
- import { Injectable, Injector, inject } from '@angular/core'
9
+ import { inject, Injectable, Injector } from '@angular/core'
10
10
  import { TranslateService } from '@ngx-translate/core'
11
11
 
12
12
  @Injectable()
@@ -21,7 +21,7 @@ export class I18nInterceptor implements HttpInterceptor {
21
21
  const translate = this.injector.get(TranslateService)
22
22
  request = request.clone({
23
23
  setHeaders: {
24
- 'Accept-Language': translate.currentLang || DEFAULT_LANG,
24
+ 'Accept-Language': translate.getCurrentLang() || DEFAULT_LANG,
25
25
  },
26
26
  })
27
27
  return next.handle(request)
@@ -1,7 +1,8 @@
1
1
  import {
2
- APP_INITIALIZER,
3
2
  EnvironmentProviders,
3
+ inject,
4
4
  makeEnvironmentProviders,
5
+ provideAppInitializer,
5
6
  Provider,
6
7
  } from '@angular/core'
7
8
  import {
@@ -22,17 +23,16 @@ import {
22
23
  } from '@angular/common/http'
23
24
  import { I18nInterceptor } from './i18n.interceptor'
24
25
 
25
- function i18nInitializerFromLocalStorageFactory(translate: TranslateService) {
26
- return () => {
27
- translate.setDefaultLang(DEFAULT_LANG)
28
- let storageLang = null
29
- try {
30
- storageLang = localStorage.getItem(LANGUAGE_STORAGE_KEY)
31
- } catch (error) {
32
- console.warn(error)
33
- }
34
- translate.use(storageLang || translate.getBrowserLang() || DEFAULT_LANG)
26
+ function i18nInitializerFromLocalStorageFactory() {
27
+ const translate = inject(TranslateService)
28
+ translate.setFallbackLang(DEFAULT_LANG)
29
+ let storageLang = null
30
+ try {
31
+ storageLang = localStorage.getItem(LANGUAGE_STORAGE_KEY)
32
+ } catch (error) {
33
+ console.warn(error)
35
34
  }
35
+ translate.use(storageLang || translate.getBrowserLang() || DEFAULT_LANG)
36
36
  }
37
37
 
38
38
  export function provideI18n(
@@ -56,12 +56,9 @@ export function provideI18n(
56
56
  provideTranslateService(usedConfig),
57
57
  ]
58
58
  if (useLocalStorage) {
59
- providers.push({
60
- provide: APP_INITIALIZER,
61
- useFactory: i18nInitializerFromLocalStorageFactory,
62
- deps: [TranslateService],
63
- multi: true,
64
- })
59
+ providers.push(
60
+ provideAppInitializer(i18nInitializerFromLocalStorageFactory)
61
+ )
65
62
  }
66
63
 
67
64
  return makeEnvironmentProviders(providers)
@@ -0,0 +1,48 @@
1
+ import { inject, Injectable, InjectionToken } from '@angular/core'
2
+ import {
3
+ provideTranslateService,
4
+ TranslateCompiler,
5
+ TranslateLoader,
6
+ TranslationObject,
7
+ } from '@ngx-translate/core'
8
+ import { Observable, of } from 'rxjs'
9
+ import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
10
+
11
+ /**
12
+ * This translate loader is used for testing purposes, it allows to provide translations directly in the test file without having to create JSON files
13
+ * Taken from https://github.com/mwootendev/ngx-translate-plugins/issues/87
14
+ */
15
+
16
+ const TestTranslations = new InjectionToken<Record<string, TranslationObject>>(
17
+ 'test-translations'
18
+ )
19
+
20
+ @Injectable()
21
+ class TestTranslateLoader implements TranslateLoader {
22
+ translations = inject(TestTranslations)
23
+ getTranslation(lang: string): Observable<TranslationObject> {
24
+ return of(this.translations[lang] as TranslationObject)
25
+ }
26
+ }
27
+
28
+ export function provideTranslateTestingService(
29
+ translations: Record<string, TranslationObject>
30
+ ) {
31
+ return provideTranslateService({
32
+ loader: [
33
+ {
34
+ provide: TestTranslations,
35
+ useValue: translations,
36
+ },
37
+ {
38
+ provide: TranslateLoader,
39
+ useClass: TestTranslateLoader,
40
+ },
41
+ ],
42
+ fallbackLang: 'en',
43
+ compiler: {
44
+ provide: TranslateCompiler,
45
+ useClass: TranslateMessageFormatCompiler,
46
+ },
47
+ })
48
+ }
@@ -171,6 +171,28 @@ export const FORMATS = {
171
171
  },
172
172
  } as const
173
173
 
174
+ marker('downloads.format.csv')
175
+ marker('downloads.format.excel')
176
+ marker('downloads.format.geojson')
177
+ marker('downloads.format.json')
178
+ marker('downloads.format.shp')
179
+ marker('downloads.format.gml')
180
+ marker('downloads.format.kml')
181
+ marker('downloads.format.gpkg')
182
+ marker('downloads.format.zip')
183
+ marker('downloads.format.pdf')
184
+ marker('downloads.format.jpg')
185
+ marker('downloads.format.png')
186
+ marker('downloads.format.tiff')
187
+ marker('downloads.format.svg')
188
+ marker('downloads.format.dxf')
189
+ marker('downloads.format.html')
190
+ marker('downloads.format.xml')
191
+ marker('downloads.format.fgb')
192
+ marker('downloads.format.jsonfg')
193
+ marker('downloads.format.webp')
194
+ marker('downloads.format.postgis')
195
+
174
196
  export type FileFormat = keyof typeof FORMATS
175
197
 
176
198
  export function getFormatPriority(linkFormat: FileFormat): number {
@@ -266,7 +288,7 @@ export function checkFileFormat(
266
288
  new RegExp(`[./]${format}`, 'i').test(link.name.toLowerCase())) ||
267
289
  ('url' in link &&
268
290
  new RegExp(`[./]${format}`, 'i').test(link.url.toString())) ||
269
- ('name' in link && link.name.toLowerCase().includes(format))
291
+ ('name' in link && new RegExp(`\\b${format}\\b`, 'i').test(link.name))
270
292
  )
271
293
  }
272
294
 
@@ -1,4 +1,4 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import { TranslateService } from '@ngx-translate/core'
3
3
  import { type Locale } from 'date-fns/locale'
4
4
  import { formatDistance } from 'date-fns/formatDistance'
@@ -30,13 +30,13 @@ export class DateService {
30
30
  locale: string
31
31
  dateObj: Date
32
32
  } {
33
- const locale = this.translateService.currentLang || DEFAULT_LANGUAGE
33
+ const locale = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE
34
34
  const dateObj = this.getDateObject(date)
35
35
  return { locale, dateObj }
36
36
  }
37
37
 
38
38
  private async getDateLocale(): Promise<Locale> {
39
- const lang = this.translateService.currentLang || DEFAULT_LANGUAGE
39
+ const lang = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE
40
40
  const locales = await this.dateLocales
41
41
  return locales[lang]
42
42
  }