geonetwork-ui 2.10.0-dev.d3efc146c → 2.10.0-dev.dc20f8516
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.
- package/fesm2022/geonetwork-ui.mjs +942 -375
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +176 -38
- package/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +9 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +7 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +8 -0
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +7 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +8 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +8 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +11 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +33 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +33 -0
- package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -0
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +68 -34
- package/src/libs/api/repository/src/lib/gn4/gn4.provider.ts +6 -1
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +46 -0
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +11 -5
- package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
- package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
- package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +5 -0
- package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +6 -0
- package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +0 -1
- package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +10 -1
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +67 -0
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +39 -0
- package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
- package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +32 -34
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +13 -13
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +18 -4
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html +8 -7
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts +6 -6
- 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
- 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
- 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
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +1 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +5 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +5 -1
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +77 -5
- package/src/libs/feature/editor/src/lib/fields.config.ts +60 -47
- package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +4 -0
- package/src/libs/feature/editor/src/lib/services/editor.service.ts +1 -1
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
- package/src/libs/feature/notify-reuse/src/index.ts +1 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +1 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +21 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
- package/src/libs/ui/elements/src/index.ts +2 -0
- package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +96 -0
- package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
- package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +41 -0
- package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +70 -0
- package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -1
- package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +0 -1
- package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +0 -4
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +28 -67
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +30 -10
- package/src/libs/ui/inputs/src/lib/button/button.component.ts +8 -0
- package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
- package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
- package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +15 -64
- package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
- package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +0 -1
- package/src/libs/util/app-config/src/lib/app-config.ts +49 -0
- package/src/libs/util/app-config/src/lib/model.ts +7 -0
- package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
- package/src/libs/util/shared/src/lib/links/link-utils.ts +1 -1
- package/src/libs/util/shared/src/lib/record/quality-score.util.ts +33 -18
- package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/src/libs/util/shared/src/lib/utils/user-display.ts +32 -0
- package/tailwind.base.css +17 -2
- package/translations/de.json +29 -20
- package/translations/en.json +29 -20
- package/translations/es.json +29 -20
- package/translations/fr.json +29 -20
- package/translations/it.json +29 -20
- package/translations/nl.json +29 -20
- package/translations/pt.json +29 -20
- package/translations/sk.json +29 -20
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core'
|
|
1
|
+
import { ChangeDetectorRef, Component, inject, Input } from '@angular/core'
|
|
2
2
|
import { CommonModule } from '@angular/common'
|
|
3
|
-
import {
|
|
4
|
-
import { GeoJSONFeatureCollection } from 'ol/format/GeoJSON.js'
|
|
5
|
-
import GeoJSON from 'ol/format/GeoJSON.js'
|
|
6
|
-
import { Polygon } from 'ol/geom.js'
|
|
7
|
-
import {
|
|
8
|
-
createViewFromLayer,
|
|
9
|
-
MapContext,
|
|
10
|
-
MapContextLayer,
|
|
11
|
-
} from '@geospatial-sdk/core'
|
|
3
|
+
import { createViewFromLayer, MapContext } from '@geospatial-sdk/core'
|
|
12
4
|
import { MapContainerComponent } from '../map-container/map-container.component'
|
|
13
|
-
import { BehaviorSubject, Observable } from 'rxjs'
|
|
14
|
-
import { switchMap } from 'rxjs/operators'
|
|
5
|
+
import { BehaviorSubject, from, Observable, of } from 'rxjs'
|
|
6
|
+
import { map, switchMap, tap } from 'rxjs/operators'
|
|
15
7
|
import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/src/lib/model/record'
|
|
8
|
+
import { createSpatialExtentLayer } from '../../map-utils'
|
|
16
9
|
|
|
17
10
|
@Component({
|
|
18
11
|
selector: 'gn-ui-spatial-extent',
|
|
@@ -22,66 +15,24 @@ import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/sr
|
|
|
22
15
|
styleUrl: './spatial-extent.component.css',
|
|
23
16
|
})
|
|
24
17
|
export class SpatialExtentComponent {
|
|
18
|
+
private _cdr = inject(ChangeDetectorRef)
|
|
19
|
+
|
|
25
20
|
@Input() set spatialExtents(value: DatasetSpatialExtent[]) {
|
|
26
21
|
this.spatialExtents$.next(value)
|
|
27
22
|
}
|
|
28
23
|
spatialExtents$ = new BehaviorSubject<DatasetSpatialExtent[]>([])
|
|
29
24
|
mapContext$: Observable<MapContext> = this.spatialExtents$.pipe(
|
|
30
|
-
switchMap(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const featureCollection: GeoJSONFeatureCollection = {
|
|
35
|
-
type: 'FeatureCollection',
|
|
36
|
-
features: [],
|
|
37
|
-
}
|
|
38
|
-
extents.forEach((extent) => {
|
|
39
|
-
if (extent.geometry) {
|
|
40
|
-
featureCollection.features.push({
|
|
41
|
-
type: 'Feature',
|
|
42
|
-
properties: {},
|
|
43
|
-
geometry: extent.geometry,
|
|
44
|
-
})
|
|
45
|
-
} else if (extent.bbox?.length >= 0) {
|
|
46
|
-
featureCollection.features.push({
|
|
47
|
-
type: 'Feature',
|
|
48
|
-
properties: {},
|
|
49
|
-
geometry: this.bboxCoordsToGeometry(extent.bbox),
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const layer: MapContextLayer = {
|
|
55
|
-
type: 'geojson',
|
|
56
|
-
data: featureCollection,
|
|
57
|
-
label: 'Spatial extents',
|
|
58
|
-
style: {
|
|
59
|
-
'stroke-color': 'black',
|
|
60
|
-
'stroke-width': 2,
|
|
61
|
-
'fill-color': 'rgba(153, 153, 153, 0.3)',
|
|
62
|
-
},
|
|
63
|
-
}
|
|
64
|
-
const view = await createViewFromLayer(layer)
|
|
65
|
-
return {
|
|
66
|
-
view,
|
|
67
|
-
layers: [layer],
|
|
25
|
+
switchMap((extents) => {
|
|
26
|
+
const layer = createSpatialExtentLayer(extents)
|
|
27
|
+
if (!layer) {
|
|
28
|
+
return of(null)
|
|
68
29
|
}
|
|
30
|
+
return from(createViewFromLayer(layer)).pipe(
|
|
31
|
+
map((view) => ({ view, layers: [layer] }) as MapContext),
|
|
32
|
+
tap(() => this._cdr.markForCheck())
|
|
33
|
+
)
|
|
69
34
|
})
|
|
70
35
|
)
|
|
71
36
|
|
|
72
37
|
error = ''
|
|
73
|
-
|
|
74
|
-
bboxCoordsToGeometry(bbox: [number, number, number, number]): Geometry {
|
|
75
|
-
const geometry = new Polygon([
|
|
76
|
-
[
|
|
77
|
-
[bbox[0], bbox[1]],
|
|
78
|
-
[bbox[0], bbox[3]],
|
|
79
|
-
[bbox[2], bbox[3]],
|
|
80
|
-
[bbox[2], bbox[1]],
|
|
81
|
-
[bbox[0], bbox[1]],
|
|
82
|
-
],
|
|
83
|
-
])
|
|
84
|
-
|
|
85
|
-
return new GeoJSON().writeGeometryObject(geometry)
|
|
86
|
-
}
|
|
87
38
|
}
|
|
@@ -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()
|
|
@@ -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,
|
|
@@ -51,6 +54,12 @@ export function getOptionalSearchConfig(): SearchConfig | null {
|
|
|
51
54
|
return searchConfig
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
let editorConfig: EditorConfig = null
|
|
58
|
+
|
|
59
|
+
export function getOptionalEditorConfig(): EditorConfig | null {
|
|
60
|
+
return editorConfig
|
|
61
|
+
}
|
|
62
|
+
|
|
54
63
|
let metadataQualityConfig: MetadataQualityConfig = null
|
|
55
64
|
export function getMetadataQualityConfig(): MetadataQualityConfig {
|
|
56
65
|
return (
|
|
@@ -285,6 +294,45 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
285
294
|
SORTABLE: parsedMetadataQualitySection.sortable,
|
|
286
295
|
} as MetadataQualityConfig)
|
|
287
296
|
|
|
297
|
+
let parsedEditingSection = parseConfigSection(
|
|
298
|
+
parsed,
|
|
299
|
+
'editing',
|
|
300
|
+
[],
|
|
301
|
+
['new_record_default_language', 'new_record_standard'],
|
|
302
|
+
warnings,
|
|
303
|
+
errors
|
|
304
|
+
)
|
|
305
|
+
if (
|
|
306
|
+
parsedEditingSection !== null &&
|
|
307
|
+
parsedEditingSection.new_record_default_language !== undefined
|
|
308
|
+
) {
|
|
309
|
+
parsedEditingSection = checkNewRecordDefaultLanguage(
|
|
310
|
+
parsedEditingSection,
|
|
311
|
+
warnings
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
if (
|
|
315
|
+
parsedEditingSection !== null &&
|
|
316
|
+
parsedEditingSection.new_record_standard !== undefined
|
|
317
|
+
) {
|
|
318
|
+
parsedEditingSection = checkNewRecordStandard(
|
|
319
|
+
parsedEditingSection,
|
|
320
|
+
warnings
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
editorConfig =
|
|
324
|
+
parsedEditingSection === null
|
|
325
|
+
? null
|
|
326
|
+
: ({
|
|
327
|
+
NEW_RECORD_DEFAULT_LANGUAGE:
|
|
328
|
+
parsedEditingSection.new_record_default_language as
|
|
329
|
+
| string
|
|
330
|
+
| undefined,
|
|
331
|
+
NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard as
|
|
332
|
+
| EditorConfig['NEW_RECORD_STANDARD']
|
|
333
|
+
| undefined,
|
|
334
|
+
} as EditorConfig)
|
|
335
|
+
|
|
288
336
|
customTranslations = parseTranslationsConfigSection(
|
|
289
337
|
parsed,
|
|
290
338
|
'translations'
|
|
@@ -309,6 +357,7 @@ export function isConfigLoaded() {
|
|
|
309
357
|
export function _reset() {
|
|
310
358
|
globalConfig = null
|
|
311
359
|
themeConfig = null
|
|
360
|
+
editorConfig = null
|
|
312
361
|
customTranslations = null
|
|
313
362
|
}
|
|
314
363
|
|
|
@@ -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
|
+
}
|
|
@@ -266,7 +266,7 @@ export function checkFileFormat(
|
|
|
266
266
|
new RegExp(`[./]${format}`, 'i').test(link.name.toLowerCase())) ||
|
|
267
267
|
('url' in link &&
|
|
268
268
|
new RegExp(`[./]${format}`, 'i').test(link.url.toString())) ||
|
|
269
|
-
('name' in link &&
|
|
269
|
+
('name' in link && new RegExp(`\\b${format}\\b`, 'i').test(link.name))
|
|
270
270
|
)
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -3,27 +3,41 @@ import {
|
|
|
3
3
|
RecordKind,
|
|
4
4
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
5
5
|
|
|
6
|
+
type TValidatorEntry = {
|
|
7
|
+
validator: (metadata: Partial<CatalogRecord>) => boolean
|
|
8
|
+
alias?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
type TValidatorMapper = {
|
|
7
|
-
[key: string]:
|
|
12
|
+
[key: string]: TValidatorEntry
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
const ValidatorMapper: TValidatorMapper = {
|
|
11
|
-
title: (record) => !!record?.title,
|
|
12
|
-
abstract: (record) => !!record?.abstract,
|
|
13
|
-
keywords: (record) => (record?.keywords?.length ?? 0) > 0,
|
|
14
|
-
legalConstraints:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
title: { validator: (record) => !!record?.title },
|
|
17
|
+
abstract: { validator: (record) => !!record?.abstract },
|
|
18
|
+
keywords: { validator: (record) => (record?.keywords?.length ?? 0) > 0 },
|
|
19
|
+
legalConstraints: {
|
|
20
|
+
validator: (record) =>
|
|
21
|
+
!!(
|
|
22
|
+
record?.legalConstraints?.length &&
|
|
23
|
+
record.legalConstraints.some((c) => c?.text?.trim().length > 0)
|
|
24
|
+
),
|
|
25
|
+
},
|
|
26
|
+
contacts: {
|
|
27
|
+
validator: (record) =>
|
|
28
|
+
!!record?.contacts?.[0]?.email &&
|
|
29
|
+
record.contacts[0].email !== 'missing@missing.com',
|
|
30
|
+
},
|
|
31
|
+
updateFrequency: {
|
|
32
|
+
validator: (record) =>
|
|
33
|
+
!!record?.updateFrequency && record.updateFrequency !== 'unknown',
|
|
34
|
+
},
|
|
35
|
+
topics: { validator: (record) => (record?.topics?.length ?? 0) > 0 },
|
|
36
|
+
organisation: {
|
|
37
|
+
validator: (record) => !!record?.contacts?.[0]?.organization?.name,
|
|
38
|
+
alias: 'contacts',
|
|
39
|
+
},
|
|
40
|
+
source: { validator: (record) => !!record?.extras?.sourcesIdentifiers },
|
|
27
41
|
} as const
|
|
28
42
|
|
|
29
43
|
export type ValidatorMapperKeys = keyof typeof ValidatorMapper & string
|
|
@@ -67,6 +81,7 @@ export function getQualityValidators(
|
|
|
67
81
|
|
|
68
82
|
return filteredProps.map((name) => ({
|
|
69
83
|
name,
|
|
70
|
-
validator: () => ValidatorMapper[name](record),
|
|
84
|
+
validator: () => ValidatorMapper[name].validator(record),
|
|
85
|
+
alias: ValidatorMapper[name].alias,
|
|
71
86
|
}))
|
|
72
87
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Feature,
|
|
3
|
+
FeatureCollection,
|
|
4
|
+
Geometry,
|
|
5
|
+
Polygon,
|
|
6
|
+
Position,
|
|
7
|
+
} from 'geojson'
|
|
8
|
+
import type { DatasetSpatialExtent } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* @returns The geometry if available, otherwise null.
|
|
@@ -94,3 +101,53 @@ export function getGeometryBoundingBox(geometry: Geometry): BoundingBox {
|
|
|
94
101
|
return coordinatesReducer(emptyExtent, geometry.coordinates)
|
|
95
102
|
}
|
|
96
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Builds the closed GeoJSON Polygon matching the given bounding box.
|
|
107
|
+
*/
|
|
108
|
+
export function bboxToPolygon(bbox: BoundingBox): Polygon {
|
|
109
|
+
const [minX, minY, maxX, maxY] = bbox
|
|
110
|
+
return {
|
|
111
|
+
type: 'Polygon',
|
|
112
|
+
coordinates: [
|
|
113
|
+
[
|
|
114
|
+
[minX, minY],
|
|
115
|
+
[minX, maxY],
|
|
116
|
+
[maxX, maxY],
|
|
117
|
+
[maxX, minY],
|
|
118
|
+
[minX, minY],
|
|
119
|
+
],
|
|
120
|
+
],
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Returns the geometry carried by a spatial extent, falling back to the polygon
|
|
126
|
+
* derived from its bounding box. Returns null when the extent has neither.
|
|
127
|
+
*/
|
|
128
|
+
export function spatialExtentToGeometry(
|
|
129
|
+
extent: DatasetSpatialExtent
|
|
130
|
+
): Geometry | null {
|
|
131
|
+
if (extent.geometry) return extent.geometry
|
|
132
|
+
if (extent.bbox) return bboxToPolygon(extent.bbox)
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Converts a list of dataset spatial extents into a GeoJSON FeatureCollection,
|
|
138
|
+
* e.g. to be used as the data source of a map layer.
|
|
139
|
+
*/
|
|
140
|
+
export function spatialExtentsToFeatureCollection(
|
|
141
|
+
extents: DatasetSpatialExtent[]
|
|
142
|
+
): FeatureCollection {
|
|
143
|
+
return {
|
|
144
|
+
type: 'FeatureCollection',
|
|
145
|
+
features: extents.reduce<Feature[]>((features, extent) => {
|
|
146
|
+
const geometry = spatialExtentToGeometry(extent)
|
|
147
|
+
if (geometry) {
|
|
148
|
+
features.push({ type: 'Feature', properties: {}, geometry })
|
|
149
|
+
}
|
|
150
|
+
return features
|
|
151
|
+
}, []),
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { UserModel } from '../../../../../../libs/common/domain/src/lib/model/user'
|
|
2
|
+
import { Individual } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
3
|
+
|
|
4
|
+
export function getIndividualDisplayName(individual: Individual): string {
|
|
5
|
+
const nameParts = [individual.firstName, individual.lastName]
|
|
6
|
+
.filter(Boolean)
|
|
7
|
+
.join(' ')
|
|
8
|
+
const orgPart = individual.organization?.name
|
|
9
|
+
? ` (${individual.organization.name})`
|
|
10
|
+
: ''
|
|
11
|
+
if (nameParts) return `${nameParts}${orgPart}`
|
|
12
|
+
return individual.organization?.name ?? individual.email ?? ''
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getAddressLines(address: string | undefined): string[] {
|
|
16
|
+
return address
|
|
17
|
+
? address
|
|
18
|
+
.split(',')
|
|
19
|
+
.map((part) => part.trim())
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
: []
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function toIndividual(user: UserModel): Individual {
|
|
25
|
+
return {
|
|
26
|
+
firstName: user.name,
|
|
27
|
+
lastName: user.surname,
|
|
28
|
+
email: user.email,
|
|
29
|
+
role: 'unspecified',
|
|
30
|
+
organization: user.organisation ? { name: user.organisation } : undefined,
|
|
31
|
+
}
|
|
32
|
+
}
|
package/tailwind.base.css
CHANGED
|
@@ -74,7 +74,10 @@
|
|
|
74
74
|
--bg-hover: var(--gn-ui-button-bg-hover, var(--color-background));
|
|
75
75
|
--font-size: var(--gn-ui-button-font-size, 1em);
|
|
76
76
|
--color: var(--gn-ui-button-color, var(--color-main));
|
|
77
|
-
|
|
77
|
+
--disabled-opacity: var(--gn-ui-button-disabled-opacity, 0.5);
|
|
78
|
+
--justify: var(--gn-ui-button-justify, center);
|
|
79
|
+
justify-content: var(--justify);
|
|
80
|
+
@apply flex flex-row items-center
|
|
78
81
|
text-[length:--font-size] leading-none
|
|
79
82
|
text-[color:--color]
|
|
80
83
|
p-[--padding] rounded-[--rounded] w-[--width] h-[--height] overflow-hidden
|
|
@@ -82,7 +85,7 @@
|
|
|
82
85
|
hover:bg-[--bg-hover] focus:bg-[--bg-hover] active:bg-[--background]
|
|
83
86
|
outline-none
|
|
84
87
|
relative
|
|
85
|
-
disabled:opacity-
|
|
88
|
+
disabled:opacity-[--disabled-opacity] disabled:pointer-events-none
|
|
86
89
|
focus:z-10 active:z-10 hover:z-10;
|
|
87
90
|
}
|
|
88
91
|
/* makes sure icons will not make the buttons grow vertically */
|
|
@@ -116,6 +119,12 @@
|
|
|
116
119
|
border-gray-100 focus:ring-4 focus:ring-gray-50;
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
.gn-ui-btn-gray-light {
|
|
123
|
+
@apply gn-ui-btn text-black
|
|
124
|
+
bg-gray-50 hover:bg-gray-100 focus:bg-gray-100 active:bg-gray-200
|
|
125
|
+
border-gray-50 focus:ring-4 focus:ring-gray-100;
|
|
126
|
+
}
|
|
127
|
+
|
|
119
128
|
.gn-ui-btn-black {
|
|
120
129
|
@apply gn-ui-btn text-white
|
|
121
130
|
bg-black hover:bg-gray-950 focus:bg-gray-950 active:bg-gray-900
|
|
@@ -134,6 +143,12 @@
|
|
|
134
143
|
bg-white border-white focus:ring-4 focus:ring-gray-300;
|
|
135
144
|
}
|
|
136
145
|
|
|
146
|
+
.gn-ui-btn-primary-light {
|
|
147
|
+
@apply gn-ui-btn text-primary-darkest
|
|
148
|
+
bg-primary-white hover:bg-primary focus:bg-primary hover:text-white focus:text-white active:bg-primary-darker
|
|
149
|
+
border-transparent;
|
|
150
|
+
}
|
|
151
|
+
|
|
137
152
|
.gn-ui-card-s {
|
|
138
153
|
@apply min-h-[64px] w-full xs:min-w-[260px] xs:max-w-[280px] sm:min-w-[300px] sm:max-w-[500px] md:min-w-[230px];
|
|
139
154
|
}
|
package/translations/de.json
CHANGED
|
@@ -150,6 +150,14 @@
|
|
|
150
150
|
"editor.record.form.draft.updateAlert": "Seit der Erstellung dieses Entwurfs wurde dieser Datensatz am { date } von { user } geändert. Durch das Veröffentlichen Ihrer Version können dessen Änderungen gelöscht werden. Um dies zu vermeiden, können Sie Ihre Änderungen rückgängig machen oder Ihre Version in voller Kenntnis der Sachlage veröffentlichen.",
|
|
151
151
|
"editor.record.form.field.abstract": "Kurzbeschreibung",
|
|
152
152
|
"editor.record.form.field.constraintsShortcuts": "",
|
|
153
|
+
"editor.record.form.field.contactDetails.email": "",
|
|
154
|
+
"editor.record.form.field.contactDetails.email.placeholder": "",
|
|
155
|
+
"editor.record.form.field.contactDetails.firstName": "",
|
|
156
|
+
"editor.record.form.field.contactDetails.firstName.placeholder": "",
|
|
157
|
+
"editor.record.form.field.contactDetails.lastName": "",
|
|
158
|
+
"editor.record.form.field.contactDetails.lastName.placeholder": "",
|
|
159
|
+
"editor.record.form.field.contactDetails.organization": "",
|
|
160
|
+
"editor.record.form.field.contactDetails.organization.placeholder": "",
|
|
153
161
|
"editor.record.form.field.contacts": "Ansprechpartner - Email",
|
|
154
162
|
"editor.record.form.field.contacts.noContact": "Bitte geben Sie mindestens einen Ansprechpartner an.",
|
|
155
163
|
"editor.record.form.field.contacts.placeholder": "Kontakt auswählen",
|
|
@@ -159,6 +167,7 @@
|
|
|
159
167
|
"editor.record.form.field.legalConstraints": "Rechtliche Einschränkung",
|
|
160
168
|
"editor.record.form.field.license": "Lizenz",
|
|
161
169
|
"editor.record.form.field.onlineLinkResources": "Beigefügte Ressourcen",
|
|
170
|
+
"editor.record.form.field.onlineLinkageResource.defaultName": "Link",
|
|
162
171
|
"editor.record.form.field.onlineResource.cancel": "Abbrechen",
|
|
163
172
|
"editor.record.form.field.onlineResource.confirm": "Bestätigen",
|
|
164
173
|
"editor.record.form.field.onlineResource.dialogTitle": "Vorschau des Datensatzes bearbeiten",
|
|
@@ -234,25 +243,25 @@
|
|
|
234
243
|
"editor.record.form.temporalExtents.addRange": "Zeitraum",
|
|
235
244
|
"editor.record.form.temporalExtents.date": "Datum",
|
|
236
245
|
"editor.record.form.temporalExtents.range": "Datumsbereich",
|
|
237
|
-
"editor.record.form.topics.
|
|
238
|
-
"editor.record.form.topics.
|
|
239
|
-
"editor.record.form.topics.
|
|
240
|
-
"editor.record.form.topics.
|
|
241
|
-
"editor.record.form.topics.
|
|
242
|
-
"editor.record.form.topics.
|
|
243
|
-
"editor.record.form.topics.
|
|
244
|
-
"editor.record.form.topics.
|
|
245
|
-
"editor.record.form.topics.
|
|
246
|
-
"editor.record.form.topics.
|
|
247
|
-
"editor.record.form.topics.
|
|
248
|
-
"editor.record.form.topics.
|
|
249
|
-
"editor.record.form.topics.
|
|
250
|
-
"editor.record.form.topics.
|
|
251
|
-
"editor.record.form.topics.
|
|
252
|
-
"editor.record.form.topics.
|
|
253
|
-
"editor.record.form.topics.
|
|
254
|
-
"editor.record.form.topics.
|
|
255
|
-
"editor.record.form.topics.
|
|
246
|
+
"editor.record.form.topics.iso.biota": "Biologie",
|
|
247
|
+
"editor.record.form.topics.iso.boundaries": "Grenzen",
|
|
248
|
+
"editor.record.form.topics.iso.climatologyMeteorologyAtmosphere": "Klimatologie/Meteorologie/Atmosphäre",
|
|
249
|
+
"editor.record.form.topics.iso.economy": "Wirtschaft",
|
|
250
|
+
"editor.record.form.topics.iso.elevation": "Höhenangaben",
|
|
251
|
+
"editor.record.form.topics.iso.environment": "Umwelt",
|
|
252
|
+
"editor.record.form.topics.iso.farming": "Landwirtschaft",
|
|
253
|
+
"editor.record.form.topics.iso.geoscientificInformation": "Geowissenschaften",
|
|
254
|
+
"editor.record.form.topics.iso.health": "Gesundheitswesen",
|
|
255
|
+
"editor.record.form.topics.iso.imageryBaseMapsEarthCover": "Bilddaten/Basiskarten/Landbedeckung",
|
|
256
|
+
"editor.record.form.topics.iso.intelligenceMilitary": "Aufklärung/Militär",
|
|
257
|
+
"editor.record.form.topics.iso.location": "Ortsangaben",
|
|
258
|
+
"editor.record.form.topics.iso.oceans": "Meere",
|
|
259
|
+
"editor.record.form.topics.iso.planningCadastre": "Planungsunterlagen/Kataster",
|
|
260
|
+
"editor.record.form.topics.iso.society": "Gesellschaft",
|
|
261
|
+
"editor.record.form.topics.iso.structure": "Bauwerke",
|
|
262
|
+
"editor.record.form.topics.iso.transportation": "Verkehrswesen",
|
|
263
|
+
"editor.record.form.topics.iso.utilitiesCommunication": "Ver- und Entsorgung/Nachrichtenwesen",
|
|
264
|
+
"editor.record.form.topics.iso.inlandWaters": "Binnengewässer",
|
|
256
265
|
"editor.record.form.topics.placeholder": "Ein oder mehrere Themen auswählen",
|
|
257
266
|
"editor.record.form.updateFrequency.planned": "Dieses Dataset wird regelmäßig aktualisiert.",
|
|
258
267
|
"editor.record.importFromExternalFile.failure.body": "Beim Importieren des Datensatzes ist ein Fehler aufgetreten:",
|
|
@@ -494,7 +503,6 @@
|
|
|
494
503
|
"record.metadata.preview.config.idle": "Standardvorschau festlegen",
|
|
495
504
|
"record.metadata.preview.config.saved": "Gespeichert!",
|
|
496
505
|
"record.metadata.preview.config.saving": "Speichern...",
|
|
497
|
-
"record.metadata.producer": "Datenproduzent",
|
|
498
506
|
"record.metadata.publication": "Veröffentlichungsdatum",
|
|
499
507
|
"record.metadata.publications": "{count, plural, =0{Veröffentlichungsdatum} one{Veröffentlichungsdatum} other{Veröffentlichungen}}",
|
|
500
508
|
"record.metadata.quality": "Metadatenqualität",
|
|
@@ -520,6 +528,7 @@
|
|
|
520
528
|
"record.metadata.quality.updateFrequency.failed": "Aktualisierungsfrequenz nicht angegeben",
|
|
521
529
|
"record.metadata.quality.updateFrequency.success": "Aktualisierungsfrequenz angegeben",
|
|
522
530
|
"record.metadata.related": "Entdecken Sie den Katalog",
|
|
531
|
+
"record.metadata.resource.contacts": "Kontakte zur Ressource",
|
|
523
532
|
"record.metadata.resourceCreated": "Erstellt",
|
|
524
533
|
"record.metadata.resourcePublished": "Veröffentlicht",
|
|
525
534
|
"record.metadata.resourceUpdated": "Zuletzt aktualisiert",
|