geonetwork-ui 2.11.0-dev.7f3cfba04 → 2.11.0-dev.840dcc932
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 +233 -18
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +43 -16
- package/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +83 -5
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +3 -3
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +15 -6
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +20 -8
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +131 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +2 -0
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +78 -1
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +11 -0
- package/src/libs/common/domain/src/lib/model/record/flags.model.ts +1 -0
- package/src/libs/common/domain/src/lib/model/record/index.ts +1 -0
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +11 -8
- package/src/libs/common/domain/src/lib/platform.service.interface.ts +1 -0
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +19 -0
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +3 -3
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints/form-field-constraints.component.ts +3 -5
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +2 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.html +18 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.ts +86 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +100 -88
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +11 -4
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +18 -4
- package/src/libs/feature/editor/src/lib/expressions.ts +67 -0
- package/src/libs/feature/editor/src/lib/fields.config.ts +13 -0
- package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -4
- package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +1 -1
- package/translations/de.json +4 -0
- package/translations/en.json +4 -0
- package/translations/es.json +4 -0
- package/translations/fr.json +4 -0
- package/translations/it.json +4 -0
- package/translations/nl.json +4 -0
- package/translations/pt.json +4 -0
- package/translations/sk.json +4 -0
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
AssociatedRecord,
|
|
37
37
|
DatasetFeatureAttribute,
|
|
38
38
|
DatasetFeatureAttributeValue,
|
|
39
|
-
|
|
39
|
+
DatasetFeatureType,
|
|
40
40
|
Individual,
|
|
41
41
|
LanguageCode,
|
|
42
42
|
SourceRecord,
|
|
@@ -428,11 +428,13 @@ function extractListedValue(): ChainableFunction<
|
|
|
428
428
|
return pipe(
|
|
429
429
|
combine(
|
|
430
430
|
pipe(findChildElement('gfc:code', false), extractCharacterString()),
|
|
431
|
-
pipe(findChildElement('gfc:label', false), extractCharacterString())
|
|
431
|
+
pipe(findChildElement('gfc:label', false), extractCharacterString()),
|
|
432
|
+
pipe(findChildElement('gfc:definition', false), extractCharacterString())
|
|
432
433
|
),
|
|
433
|
-
map(([code, label]) => ({
|
|
434
|
-
|
|
434
|
+
map(([code, label, description]) => ({
|
|
435
|
+
value: code ?? '',
|
|
435
436
|
...(label && { label }),
|
|
437
|
+
...(description && { description }),
|
|
436
438
|
}))
|
|
437
439
|
)
|
|
438
440
|
}
|
|
@@ -447,6 +449,10 @@ function extractFeatureAttribute(): ChainableFunction<
|
|
|
447
449
|
findChildElement('gfc:memberName', false),
|
|
448
450
|
extractCharacterStringOrText()
|
|
449
451
|
),
|
|
452
|
+
pipe(
|
|
453
|
+
findChildElement('gfc:designation', false),
|
|
454
|
+
extractCharacterString()
|
|
455
|
+
),
|
|
450
456
|
pipe(findChildElement('gfc:definition', false), extractCharacterString()),
|
|
451
457
|
pipe(findChildElement('gfc:code', false), extractCharacterString()),
|
|
452
458
|
pipe(
|
|
@@ -456,11 +462,12 @@ function extractFeatureAttribute(): ChainableFunction<
|
|
|
456
462
|
pipe(
|
|
457
463
|
findNestedElements('gfc:listedValue', 'gfc:FC_ListedValue'),
|
|
458
464
|
mapArray(extractListedValue()),
|
|
459
|
-
filterArray((value) => !!(value.
|
|
465
|
+
filterArray((value) => !!(value.value || value.label))
|
|
460
466
|
)
|
|
461
467
|
),
|
|
462
|
-
map(([name, description, code, type, values]) => ({
|
|
468
|
+
map(([name, label, description, code, type, values]) => ({
|
|
463
469
|
name: name ?? '',
|
|
470
|
+
...(label && { label }),
|
|
464
471
|
...(description && { description }),
|
|
465
472
|
...(code && { code }),
|
|
466
473
|
...(type && { type }),
|
|
@@ -471,7 +478,7 @@ function extractFeatureAttribute(): ChainableFunction<
|
|
|
471
478
|
|
|
472
479
|
export function readFeatureTypeDescriptions(
|
|
473
480
|
rootEl: XmlElement
|
|
474
|
-
):
|
|
481
|
+
): Array<DatasetFeatureType> {
|
|
475
482
|
return pipe(
|
|
476
483
|
findNestedElements(
|
|
477
484
|
'mdb:contentInfo',
|
|
@@ -488,6 +495,10 @@ export function readFeatureTypeDescriptions(
|
|
|
488
495
|
findChildElement('gfc:typeName', false),
|
|
489
496
|
extractCharacterStringOrText()
|
|
490
497
|
),
|
|
498
|
+
pipe(
|
|
499
|
+
findChildElement('gfc:designation', false),
|
|
500
|
+
extractCharacterString()
|
|
501
|
+
),
|
|
491
502
|
pipe(
|
|
492
503
|
findChildElement('gfc:definition', false),
|
|
493
504
|
extractCharacterString()
|
|
@@ -500,8 +511,9 @@ export function readFeatureTypeDescriptions(
|
|
|
500
511
|
mapArray(extractFeatureAttribute())
|
|
501
512
|
)
|
|
502
513
|
),
|
|
503
|
-
map(([name, description, attributes]) => ({
|
|
514
|
+
map(([name, label, description, attributes]) => ({
|
|
504
515
|
name: name ?? '',
|
|
516
|
+
...(label && { label }),
|
|
505
517
|
...(description && { description }),
|
|
506
518
|
attributes,
|
|
507
519
|
}))
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CatalogRecord,
|
|
3
|
+
DatasetFeatureAttribute,
|
|
4
|
+
DatasetFeatureAttributeValue,
|
|
5
|
+
DatasetFeatureType,
|
|
3
6
|
DatasetRecord,
|
|
4
7
|
Individual,
|
|
5
8
|
LanguageCode,
|
|
@@ -616,3 +619,131 @@ export function writeAssociatedRecords(
|
|
|
616
619
|
)
|
|
617
620
|
)(rootEl)
|
|
618
621
|
}
|
|
622
|
+
|
|
623
|
+
function createNilElement(name: string, nilReason: string) {
|
|
624
|
+
return pipe(createElement(name), writeAttribute('gco:nilReason', nilReason))
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function createFeatureCatalogueShell() {
|
|
628
|
+
return pipe(
|
|
629
|
+
createNestedElement(
|
|
630
|
+
'mdb:contentInfo',
|
|
631
|
+
'mrc:MD_FeatureCatalogue',
|
|
632
|
+
'mrc:featureCatalogue',
|
|
633
|
+
'gfc:FC_FeatureCatalogue'
|
|
634
|
+
),
|
|
635
|
+
// create mandatory elements which are not part of the model yet
|
|
636
|
+
appendChildren(
|
|
637
|
+
createNilElement('cat:name', 'missing'),
|
|
638
|
+
createNilElement('cat:scope', 'missing'),
|
|
639
|
+
createNilElement('cat:versionNumber', 'missing'),
|
|
640
|
+
createNilElement('cat:versionDate', 'missing'),
|
|
641
|
+
createNilElement('gfc:producer', 'missing')
|
|
642
|
+
)
|
|
643
|
+
)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function createListedValue(value: DatasetFeatureAttributeValue) {
|
|
647
|
+
return pipe(
|
|
648
|
+
createNestedElement('gfc:listedValue', 'gfc:FC_ListedValue'),
|
|
649
|
+
appendChildren(
|
|
650
|
+
value.label
|
|
651
|
+
? pipe(createElement('gfc:label'), writeCharacterString(value.label))
|
|
652
|
+
: createNilElement('gfc:label', 'missing'),
|
|
653
|
+
value.value
|
|
654
|
+
? pipe(createElement('gfc:code'), writeCharacterString(value.value))
|
|
655
|
+
: null,
|
|
656
|
+
value.description
|
|
657
|
+
? pipe(
|
|
658
|
+
createElement('gfc:definition'),
|
|
659
|
+
writeCharacterString(value.description)
|
|
660
|
+
)
|
|
661
|
+
: null
|
|
662
|
+
)
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function createFeatureAttribute(attribute: DatasetFeatureAttribute) {
|
|
667
|
+
return pipe(
|
|
668
|
+
createNestedElement(
|
|
669
|
+
'gfc:carrierOfCharacteristics',
|
|
670
|
+
'gfc:FC_FeatureAttribute'
|
|
671
|
+
),
|
|
672
|
+
appendChildren(
|
|
673
|
+
pipe(createElement('gfc:memberName'), setTextContent(attribute.name)),
|
|
674
|
+
attribute.description
|
|
675
|
+
? pipe(
|
|
676
|
+
createElement('gfc:definition'),
|
|
677
|
+
writeCharacterString(attribute.description)
|
|
678
|
+
)
|
|
679
|
+
: null,
|
|
680
|
+
createNilElement('gfc:cardinality', 'missing'),
|
|
681
|
+
attribute.label
|
|
682
|
+
? pipe(
|
|
683
|
+
createElement('gfc:designation'),
|
|
684
|
+
writeCharacterString(attribute.label)
|
|
685
|
+
)
|
|
686
|
+
: null,
|
|
687
|
+
attribute.code
|
|
688
|
+
? pipe(createElement('gfc:code'), writeCharacterString(attribute.code))
|
|
689
|
+
: null,
|
|
690
|
+
attribute.type
|
|
691
|
+
? pipe(
|
|
692
|
+
createNestedElement('gfc:valueType', 'gco:TypeName', 'gco:aName'),
|
|
693
|
+
writeCharacterString(attribute.type)
|
|
694
|
+
)
|
|
695
|
+
: null,
|
|
696
|
+
...(attribute.values ?? [])
|
|
697
|
+
.filter((value) => value.value || value.label)
|
|
698
|
+
.map(createListedValue)
|
|
699
|
+
)
|
|
700
|
+
)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function createFeatureType(featureType: DatasetFeatureType) {
|
|
704
|
+
return pipe(
|
|
705
|
+
createNestedElement('gfc:featureType', 'gfc:FC_FeatureType'),
|
|
706
|
+
appendChildren(
|
|
707
|
+
pipe(createElement('gfc:typeName'), setTextContent(featureType.name)),
|
|
708
|
+
featureType.description
|
|
709
|
+
? pipe(
|
|
710
|
+
createElement('gfc:definition'),
|
|
711
|
+
writeCharacterString(featureType.description)
|
|
712
|
+
)
|
|
713
|
+
: null,
|
|
714
|
+
pipe(
|
|
715
|
+
createNestedElement('gfc:isAbstract', 'gco:Boolean'),
|
|
716
|
+
setTextContent('false')
|
|
717
|
+
),
|
|
718
|
+
featureType.label
|
|
719
|
+
? pipe(
|
|
720
|
+
createElement('gfc:designation'),
|
|
721
|
+
writeCharacterString(featureType.label)
|
|
722
|
+
)
|
|
723
|
+
: null,
|
|
724
|
+
...featureType.attributes.map(createFeatureAttribute),
|
|
725
|
+
createNilElement('gfc:featureCatalogue', 'missing')
|
|
726
|
+
)
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
export function writeFeatureTypeDescriptions(
|
|
731
|
+
record: DatasetRecord,
|
|
732
|
+
rootEl: XmlElement
|
|
733
|
+
) {
|
|
734
|
+
const featureTypes = record.featureTypeDescriptions ?? []
|
|
735
|
+
const catalogues = findNestedElements(
|
|
736
|
+
'mdb:contentInfo',
|
|
737
|
+
'mrc:MD_FeatureCatalogue',
|
|
738
|
+
'mrc:featureCatalogue',
|
|
739
|
+
'gfc:FC_FeatureCatalogue'
|
|
740
|
+
)(rootEl)
|
|
741
|
+
catalogues.forEach((catalogue) =>
|
|
742
|
+
removeChildrenByName('gfc:featureType')(catalogue)
|
|
743
|
+
)
|
|
744
|
+
if (!featureTypes.length) return
|
|
745
|
+
|
|
746
|
+
const catalogue =
|
|
747
|
+
catalogues[0] ?? appendChildTree(createFeatureCatalogueShell())(rootEl)
|
|
748
|
+
appendChildren(...featureTypes.map(createFeatureType))(catalogue)
|
|
749
|
+
}
|
|
@@ -423,6 +423,8 @@ export class Iso19139Converter extends BaseConverter<string> {
|
|
|
423
423
|
|
|
424
424
|
if (record.kind === 'dataset') {
|
|
425
425
|
fieldChanged('status') && this.writers['status'](record, rootEl)
|
|
426
|
+
fieldChanged('featureTypeDescriptions') &&
|
|
427
|
+
this.writers['featureTypeDescriptions'](record, rootEl)
|
|
426
428
|
}
|
|
427
429
|
if (record.kind === 'dataset' || record.kind === 'reuse') {
|
|
428
430
|
fieldChanged('updateFrequency') &&
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DatasetFeatureCatalog,
|
|
20
20
|
LanguageCode,
|
|
21
21
|
LinkedRecord,
|
|
22
|
+
RecordFlag,
|
|
22
23
|
RecordRelation,
|
|
23
24
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
24
25
|
import {
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
LanguagesApiService,
|
|
37
38
|
RecordsApiService,
|
|
38
39
|
SearchApiService,
|
|
40
|
+
TagsApiService,
|
|
39
41
|
} from '../../../../../../libs/data-access/gn4/src'
|
|
40
42
|
import {
|
|
41
43
|
combineLatest,
|
|
@@ -48,7 +50,13 @@ import {
|
|
|
48
50
|
switchMap,
|
|
49
51
|
throwError,
|
|
50
52
|
} from 'rxjs'
|
|
51
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
catchError,
|
|
55
|
+
map,
|
|
56
|
+
shareReplay,
|
|
57
|
+
tap,
|
|
58
|
+
withLatestFrom,
|
|
59
|
+
} from 'rxjs/operators'
|
|
52
60
|
import { lt } from 'semver'
|
|
53
61
|
import { ElasticsearchService } from './elasticsearch'
|
|
54
62
|
import { toLang2 } from '../../../../../../libs/util/i18n/src'
|
|
@@ -80,6 +88,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
80
88
|
private platformService = inject(PlatformServiceInterface)
|
|
81
89
|
private gn4LanguagesApi = inject(LanguagesApiService)
|
|
82
90
|
private settingsService = inject(Gn4SettingsService)
|
|
91
|
+
private gn4TagsApi = inject(TagsApiService)
|
|
83
92
|
private disableDraft = inject(DISABLE_DRAFT, { optional: true }) ?? false
|
|
84
93
|
private defaultConverter = inject(DEFAULT_RECORD_CONVERTER)
|
|
85
94
|
|
|
@@ -675,4 +684,72 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
675
684
|
private removeRecordFromLocalStorage(recordId: string): void {
|
|
676
685
|
window.localStorage.removeItem(this.getLocalStorageKeyForRecord(recordId))
|
|
677
686
|
}
|
|
687
|
+
|
|
688
|
+
/*
|
|
689
|
+
* FLAGS
|
|
690
|
+
* Note: in GN4 API, categories are named "tags"
|
|
691
|
+
*/
|
|
692
|
+
|
|
693
|
+
categoriesIdentifierForFlags: Observable<Record<RecordFlag, number>> =
|
|
694
|
+
this.gn4TagsApi.getTags().pipe(
|
|
695
|
+
map((categories) =>
|
|
696
|
+
categories.reduce(
|
|
697
|
+
(acc, category) => {
|
|
698
|
+
if (category.name.startsWith('geonetwork-ui-flag:')) {
|
|
699
|
+
acc[
|
|
700
|
+
category.name.replace(/^geonetwork-ui-flag:/, '') as RecordFlag
|
|
701
|
+
] = category.id
|
|
702
|
+
}
|
|
703
|
+
return acc
|
|
704
|
+
},
|
|
705
|
+
{} as Record<RecordFlag, number>
|
|
706
|
+
)
|
|
707
|
+
),
|
|
708
|
+
shareReplay(1)
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
private getCategoryIdForFlag(
|
|
712
|
+
flagName: RecordFlag
|
|
713
|
+
): Observable<number | null> {
|
|
714
|
+
return this.categoriesIdentifierForFlags.pipe(
|
|
715
|
+
map((categories) => {
|
|
716
|
+
const categoryId = categories[flagName]
|
|
717
|
+
if (categoryId === undefined) {
|
|
718
|
+
console.warn(
|
|
719
|
+
`The record flag ${flagName} is not created on GeoNetwork backend; please create a category with the name geonetwork-ui-flag:${flagName} for this to work.`
|
|
720
|
+
)
|
|
721
|
+
return null
|
|
722
|
+
}
|
|
723
|
+
return categoryId
|
|
724
|
+
})
|
|
725
|
+
)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
setRecordFlag(
|
|
729
|
+
uniqueIdentifier: string,
|
|
730
|
+
flagName: RecordFlag,
|
|
731
|
+
value: boolean
|
|
732
|
+
): Observable<void> {
|
|
733
|
+
return this.getCategoryIdForFlag(flagName).pipe(
|
|
734
|
+
switchMap((categoryId) => {
|
|
735
|
+
if (categoryId === null) return of()
|
|
736
|
+
return value
|
|
737
|
+
? this.gn4RecordsApi.tagRecord(uniqueIdentifier, [categoryId], false)
|
|
738
|
+
: this.gn4RecordsApi.deleteTags(uniqueIdentifier, [categoryId])
|
|
739
|
+
})
|
|
740
|
+
)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
getRecordFlag(
|
|
744
|
+
uniqueIdentifier: string,
|
|
745
|
+
flagName: RecordFlag
|
|
746
|
+
): Observable<boolean> {
|
|
747
|
+
return this.gn4RecordsApi.getRecordTags(uniqueIdentifier).pipe(
|
|
748
|
+
withLatestFrom(this.getCategoryIdForFlag(flagName)),
|
|
749
|
+
map(([tags, categoryId]) => {
|
|
750
|
+
if (categoryId === null) return false
|
|
751
|
+
return Array.from(tags).some((tag) => tag.id === categoryId)
|
|
752
|
+
})
|
|
753
|
+
)
|
|
754
|
+
}
|
|
678
755
|
}
|
|
@@ -92,6 +92,13 @@ export class Gn4PlatformService implements PlatformServiceInterface {
|
|
|
92
92
|
switchMap((apiUser) => this.mapper.userFromMeApi(apiUser)),
|
|
93
93
|
shareReplay({ bufferSize: 1, refCount: true })
|
|
94
94
|
)
|
|
95
|
+
private isAdmin$ = this.disableAuth
|
|
96
|
+
? of(false)
|
|
97
|
+
: of(true).pipe(
|
|
98
|
+
switchMap(() => this.meApi.getMe()),
|
|
99
|
+
map((apiUser) => !!apiUser?.admin),
|
|
100
|
+
shareReplay({ bufferSize: 1, refCount: true })
|
|
101
|
+
)
|
|
95
102
|
|
|
96
103
|
/**
|
|
97
104
|
* A map of already loaded thesauri (groups of keywords); the key is a URI
|
|
@@ -146,6 +153,10 @@ export class Gn4PlatformService implements PlatformServiceInterface {
|
|
|
146
153
|
return this.isUserAnonymous$
|
|
147
154
|
}
|
|
148
155
|
|
|
156
|
+
isAdministrator(): Observable<boolean> {
|
|
157
|
+
return this.isAdmin$
|
|
158
|
+
}
|
|
159
|
+
|
|
149
160
|
getOrganizations(): Observable<Organization[]> {
|
|
150
161
|
return undefined
|
|
151
162
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type RecordFlag = 'IS_REFERENCE_DATASET'
|
|
@@ -316,7 +316,7 @@ export interface DatasetRecord extends BaseRecord {
|
|
|
316
316
|
spatialExtents: Array<DatasetSpatialExtent>
|
|
317
317
|
temporalExtents: Array<DatasetTemporalExtent>
|
|
318
318
|
spatialRepresentation?: SpatialRepresentationType
|
|
319
|
-
featureTypeDescriptions?:
|
|
319
|
+
featureTypeDescriptions?: Array<DatasetFeatureType>
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
export interface ServiceEndpoint {
|
|
@@ -348,20 +348,23 @@ export interface ReuseRecord extends BaseRecord {
|
|
|
348
348
|
temporalExtents: Array<DatasetTemporalExtent>
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
export interface DatasetFeatureType {
|
|
352
|
+
name: string
|
|
353
|
+
label?: string
|
|
354
|
+
description?: string
|
|
355
|
+
attributes: DatasetFeatureAttribute[]
|
|
356
|
+
}
|
|
351
357
|
export type DatasetFeatureCatalog = {
|
|
352
|
-
featureTypes: Array<
|
|
353
|
-
name: string
|
|
354
|
-
description?: string
|
|
355
|
-
attributes: DatasetFeatureAttribute[]
|
|
356
|
-
}>
|
|
358
|
+
featureTypes: Array<DatasetFeatureType>
|
|
357
359
|
}
|
|
358
360
|
export interface DatasetFeatureAttributeValue {
|
|
359
|
-
|
|
360
|
-
description?: string
|
|
361
|
+
value: string // empty string when the value has no code
|
|
361
362
|
label?: string
|
|
363
|
+
description?: string
|
|
362
364
|
}
|
|
363
365
|
export interface DatasetFeatureAttribute {
|
|
364
366
|
name: string
|
|
367
|
+
label?: string
|
|
365
368
|
description?: string
|
|
366
369
|
type?: string
|
|
367
370
|
code?: string
|
|
@@ -28,6 +28,7 @@ export abstract class PlatformServiceInterface {
|
|
|
28
28
|
abstract supportsAuthentication(): boolean
|
|
29
29
|
abstract getMe(): Observable<UserModel>
|
|
30
30
|
abstract isAnonymous(): Observable<boolean>
|
|
31
|
+
abstract isAdministrator(): Observable<boolean>
|
|
31
32
|
abstract getUsers(): Observable<UserModel[]>
|
|
32
33
|
abstract getUserPermissionsByGroup(): Observable<GroupModel[]>
|
|
33
34
|
abstract getUsersByOrganization(
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
DatasetFeatureCatalog,
|
|
12
12
|
LanguageCode,
|
|
13
13
|
LinkedRecord,
|
|
14
|
+
RecordFlag,
|
|
14
15
|
} from '../model/record'
|
|
15
16
|
|
|
16
17
|
export abstract class RecordsRepositoryInterface {
|
|
@@ -106,4 +107,22 @@ export abstract class RecordsRepositoryInterface {
|
|
|
106
107
|
): Observable<{ user: string; date: Date }>
|
|
107
108
|
abstract getRecordPublicationStatus(uuid: string): Observable<boolean>
|
|
108
109
|
abstract getApplicationLanguages(): Observable<LanguageCode[]>
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Sets a given flag on a record. Flags are not part of the metadata document, which is why they are
|
|
113
|
+
* not part of the Record model.
|
|
114
|
+
*/
|
|
115
|
+
abstract setRecordFlag(
|
|
116
|
+
uniqueIdentifier: string,
|
|
117
|
+
flagName: RecordFlag,
|
|
118
|
+
value: boolean
|
|
119
|
+
): Observable<void>
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Checks whether a given flag is enabled for a record.
|
|
123
|
+
*/
|
|
124
|
+
abstract getRecordFlag(
|
|
125
|
+
uniqueIdentifier: string,
|
|
126
|
+
flagName: RecordFlag
|
|
127
|
+
): Observable<boolean>
|
|
109
128
|
}
|
|
@@ -182,9 +182,9 @@ As such, **it is not very interesting at all.**`,
|
|
|
182
182
|
code: 'NOM',
|
|
183
183
|
type: 'String (48)',
|
|
184
184
|
values: [
|
|
185
|
-
{
|
|
186
|
-
{
|
|
187
|
-
{
|
|
185
|
+
{ value: 'Pomme', label: 'Les Pommiers' },
|
|
186
|
+
{ value: 'Cotton', label: 'Rue Cotton' },
|
|
187
|
+
{ value: "Passage de l'échiquier" },
|
|
188
188
|
],
|
|
189
189
|
},
|
|
190
190
|
{
|
|
@@ -9,10 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import { SortableListComponent } from '../../../../../../../../../libs/ui/layout/src'
|
|
11
11
|
import { ConstraintCardComponent } from '../../../constraint-card/constraint-card.component'
|
|
12
|
-
import {
|
|
13
|
-
CatalogRecordKeys,
|
|
14
|
-
Constraint,
|
|
15
|
-
} from '../../../../../../../../../libs/common/domain/src/lib/model/record'
|
|
12
|
+
import { Constraint } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
|
|
16
13
|
import { ButtonComponent } from '../../../../../../../../../libs/ui/inputs/src'
|
|
17
14
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
18
15
|
import { TranslatePipe } from '@ngx-translate/core'
|
|
@@ -22,6 +19,7 @@ import {
|
|
|
22
19
|
provideNgIconsConfig,
|
|
23
20
|
} from '@ng-icons/core'
|
|
24
21
|
import { iconoirPlus } from '@ng-icons/iconoir'
|
|
22
|
+
import { FormFieldModel } from '../../../../models'
|
|
25
23
|
|
|
26
24
|
marker('editor.record.form.constraint.add.legalConstraints')
|
|
27
25
|
marker('editor.record.form.constraint.add.securityConstraints')
|
|
@@ -53,7 +51,7 @@ marker('editor.record.form.constraint.header.otherConstraints')
|
|
|
53
51
|
export class FormFieldConstraintsComponent implements OnInit {
|
|
54
52
|
@Input() label: string
|
|
55
53
|
@Input() value: Constraint[]
|
|
56
|
-
@Input() constraintType:
|
|
54
|
+
@Input() constraintType: FormFieldModel
|
|
57
55
|
@Output() valueChange = new EventEmitter<Constraint[]>()
|
|
58
56
|
|
|
59
57
|
constraintsHeader = ''
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
} from '../../../../../../../../../libs/util/shared/src'
|
|
37
37
|
import { map } from 'rxjs/operators'
|
|
38
38
|
import { SortableListComponent } from '../../../../../../../../../libs/ui/layout/src'
|
|
39
|
-
import {
|
|
39
|
+
import { FormFieldModelSpecifier } from '../../../../models/editor-config.model'
|
|
40
40
|
|
|
41
41
|
@Component({
|
|
42
42
|
selector: 'gn-ui-form-field-contacts',
|
|
@@ -59,7 +59,7 @@ export class FormFieldContactsComponent implements OnDestroy, OnChanges {
|
|
|
59
59
|
private changeDetectorRef = inject(ChangeDetectorRef)
|
|
60
60
|
|
|
61
61
|
@Input() value: Individual[]
|
|
62
|
-
@Input() modelSpecifier:
|
|
62
|
+
@Input() modelSpecifier: FormFieldModelSpecifier
|
|
63
63
|
@Output() valueChange: EventEmitter<Individual[]> = new EventEmitter()
|
|
64
64
|
|
|
65
65
|
contacts: Individual[] = []
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div class="flex-1 flex justify-between items-center">
|
|
2
|
+
<gn-ui-check-toggle
|
|
3
|
+
[label]="labelKey() | translate"
|
|
4
|
+
[value]="initialValue$ | async"
|
|
5
|
+
(toggled)="onToggle($event)"
|
|
6
|
+
[attr.data-cy]="'record-flag-toggle-' + flagName()"
|
|
7
|
+
></gn-ui-check-toggle>
|
|
8
|
+
|
|
9
|
+
@if (hintKey()) {
|
|
10
|
+
<ng-icon
|
|
11
|
+
name="matHelpOutline"
|
|
12
|
+
size="16"
|
|
13
|
+
class="m-2"
|
|
14
|
+
[matTooltip]="hintKey() | translate"
|
|
15
|
+
matTooltipPosition="above"
|
|
16
|
+
></ng-icon>
|
|
17
|
+
}
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
inject,
|
|
5
|
+
input,
|
|
6
|
+
} from '@angular/core'
|
|
7
|
+
import { AsyncPipe } from '@angular/common'
|
|
8
|
+
import { CheckToggleComponent } from '../../../../../../../../../libs/ui/inputs/src'
|
|
9
|
+
import { EditorFacade } from '../../../../+state/editor.facade'
|
|
10
|
+
import { TranslatePipe, TranslateService } from '@ngx-translate/core'
|
|
11
|
+
import { RecordFlag } from '../../../../../../../../../libs/common/domain/src/lib/model'
|
|
12
|
+
import { RecordsRepositoryInterface } from '../../../../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
|
|
13
|
+
import { toSignal } from '@angular/core/rxjs-interop'
|
|
14
|
+
import { filter } from 'rxjs'
|
|
15
|
+
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
16
|
+
import { MatTooltip } from '@angular/material/tooltip'
|
|
17
|
+
import { map, switchMap } from 'rxjs/operators'
|
|
18
|
+
import { matHelpOutline } from '@ng-icons/material-icons/outline'
|
|
19
|
+
import { NotificationsService } from '../../../../../../../../../libs/feature/notifications/src'
|
|
20
|
+
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'gn-ui-form-field-record-flag',
|
|
23
|
+
standalone: true,
|
|
24
|
+
imports: [AsyncPipe, CheckToggleComponent, TranslatePipe, NgIcon, MatTooltip],
|
|
25
|
+
viewProviders: [
|
|
26
|
+
provideIcons({
|
|
27
|
+
matHelpOutline,
|
|
28
|
+
}),
|
|
29
|
+
],
|
|
30
|
+
templateUrl: './form-field-record-flag-component.html',
|
|
31
|
+
styleUrls: ['./form-field-record-flag-component.css'],
|
|
32
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
33
|
+
})
|
|
34
|
+
export class FormFieldRecordFlagComponent {
|
|
35
|
+
flagName = input<RecordFlag>()
|
|
36
|
+
labelKey = input<string>()
|
|
37
|
+
hintKey = input<string>()
|
|
38
|
+
|
|
39
|
+
private editorFacade = inject(EditorFacade)
|
|
40
|
+
private repository = inject(RecordsRepositoryInterface)
|
|
41
|
+
private notificationsService = inject(NotificationsService)
|
|
42
|
+
private translateService = inject(TranslateService)
|
|
43
|
+
|
|
44
|
+
private record = toSignal(this.editorFacade.record$)
|
|
45
|
+
|
|
46
|
+
recordKind = toSignal(
|
|
47
|
+
this.editorFacade.record$.pipe(map((record) => record.kind))
|
|
48
|
+
)
|
|
49
|
+
initialValue$ = this.editorFacade.record$.pipe(
|
|
50
|
+
filter((record) => !!record),
|
|
51
|
+
switchMap((record) =>
|
|
52
|
+
this.repository.getRecordFlag(record.uniqueIdentifier, this.flagName())
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
onToggle(enabled: boolean) {
|
|
57
|
+
if (this.record() === null) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
this.repository
|
|
61
|
+
.setRecordFlag(this.record().uniqueIdentifier, this.flagName(), enabled)
|
|
62
|
+
.subscribe({
|
|
63
|
+
next: () => {
|
|
64
|
+
// do nothing
|
|
65
|
+
},
|
|
66
|
+
error: (error) => {
|
|
67
|
+
this.notificationsService.showNotification(
|
|
68
|
+
{
|
|
69
|
+
type: 'error',
|
|
70
|
+
title: this.translateService.instant(
|
|
71
|
+
'editor.record.flag.saveError.title'
|
|
72
|
+
),
|
|
73
|
+
text: `${this.translateService.instant(
|
|
74
|
+
'editor.record.flag.saveError.body',
|
|
75
|
+
{
|
|
76
|
+
recordKind: this.recordKind(),
|
|
77
|
+
}
|
|
78
|
+
)} ${error.message}`,
|
|
79
|
+
},
|
|
80
|
+
undefined,
|
|
81
|
+
error
|
|
82
|
+
)
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|