geonetwork-ui 2.2.0-dev.eaf94daa → 2.2.0-dev.ecc0ab67
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/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +42 -51
- package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.mjs +4 -3
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/converter.mjs +16 -11
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +23 -16
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +21 -18
- package/esm2022/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.mjs +10 -3
- package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.mjs +14 -5
- package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.mjs +33 -8
- package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
- package/esm2022/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.mjs +1 -1
- package/esm2022/libs/common/domain/src/lib/platform.service.interface.mjs +1 -1
- package/esm2022/libs/feature/search/src/lib/utils/service/fields.mjs +41 -26
- package/esm2022/libs/feature/search/src/lib/utils/service/fields.service.mjs +11 -9
- package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +2 -2
- package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +35 -22
- package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +2 -3
- package/esm2022/libs/ui/elements/src/lib/record-api-form/record-api-form.component.mjs +9 -6
- package/esm2022/translations/de.json +2 -1
- package/esm2022/translations/en.json +4 -3
- package/esm2022/translations/es.json +2 -1
- package/esm2022/translations/fr.json +4 -3
- package/esm2022/translations/it.json +4 -3
- package/esm2022/translations/nl.json +2 -1
- package/esm2022/translations/pt.json +2 -1
- package/fesm2022/geonetwork-ui.mjs +254 -170
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts +0 -1
- package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/converter.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +5 -4
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +3 -2
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.d.ts +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts +9 -2
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +5 -10
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.d.ts +1 -0
- package/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.d.ts.map +1 -1
- package/libs/common/domain/src/lib/platform.service.interface.d.ts +1 -1
- package/libs/common/domain/src/lib/platform.service.interface.d.ts.map +1 -1
- package/libs/feature/search/src/lib/utils/service/fields.d.ts +21 -8
- package/libs/feature/search/src/lib/utils/service/fields.d.ts.map +1 -1
- package/libs/feature/search/src/lib/utils/service/fields.service.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts +7 -3
- package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts +3 -1
- package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +6 -6
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +5 -3
- package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +22 -4
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +69 -65
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.ts +3 -2
- package/src/libs/api/metadata-converter/src/lib/iso19139/converter.ts +19 -12
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +62 -43
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +91 -59
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +22 -2
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts +15 -4
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +47 -8
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +5 -11
- package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +1 -0
- package/src/libs/common/domain/src/lib/platform.service.interface.ts +1 -4
- package/src/libs/common/fixtures/src/lib/gn4/groups.fixtures.ts +1 -1
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +6 -2
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +21 -16
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +43 -34
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.css +2 -2
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +45 -18
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +31 -12
- package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +1 -2
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +5 -5
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +12 -4
- package/translations/de.json +2 -1
- package/translations/en.json +4 -3
- package/translations/es.json +2 -1
- package/translations/fr.json +4 -3
- package/translations/it.json +4 -3
- package/translations/nl.json +2 -1
- package/translations/pt.json +2 -1
- package/translations/sk.json +2 -1
package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts
CHANGED
|
@@ -25,7 +25,14 @@ import {
|
|
|
25
25
|
SourceWithUnknownProps,
|
|
26
26
|
} from '../../../../../../../libs/api/metadata-converter/src'
|
|
27
27
|
import { combineLatest, Observable, of, switchMap, takeLast } from 'rxjs'
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
filter,
|
|
30
|
+
map,
|
|
31
|
+
shareReplay,
|
|
32
|
+
startWith,
|
|
33
|
+
tap,
|
|
34
|
+
withLatestFrom,
|
|
35
|
+
} from 'rxjs/operators'
|
|
29
36
|
import { LangService } from '../../../../../../../libs/util/i18n/src'
|
|
30
37
|
import { PlatformServiceInterface } from '../../../../../../../libs/common/domain/src/lib/platform.service.interface'
|
|
31
38
|
import { coerce, satisfies, valid } from 'semver'
|
|
@@ -296,15 +303,28 @@ export class OrganizationsFromMetadataService
|
|
|
296
303
|
|
|
297
304
|
const ownerOrganization = allContactOrgs[0]
|
|
298
305
|
|
|
306
|
+
// read the owner group as well to have a fallback logo
|
|
307
|
+
const groupId = selectField(source, 'groupOwner')
|
|
308
|
+
const group$ = this.groups$.pipe(
|
|
309
|
+
map((groups) =>
|
|
310
|
+
groups.find((group) => {
|
|
311
|
+
return group.id === Number(groupId)
|
|
312
|
+
})
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
299
316
|
return this.organisations$.pipe(
|
|
300
317
|
takeLast(1),
|
|
301
|
-
|
|
318
|
+
withLatestFrom(group$),
|
|
319
|
+
map(([organisations, group]: [Organization[], GroupApiModel]) => {
|
|
302
320
|
const recordOrganisation = organisations.filter(
|
|
303
321
|
(org) => org.name === ownerOrganization.name
|
|
304
322
|
)[0]
|
|
323
|
+
const logoUrl = group?.logo && getAsUrl(`${IMAGE_URL}${group.logo}`)
|
|
305
324
|
return {
|
|
306
325
|
...record,
|
|
307
326
|
ownerOrganization: {
|
|
327
|
+
logoUrl,
|
|
308
328
|
...ownerOrganization,
|
|
309
329
|
...recordOrganisation,
|
|
310
330
|
},
|
|
@@ -45,12 +45,23 @@ export class Gn4PlatformMapper {
|
|
|
45
45
|
return { ...apiUser, id: id.toString() } as UserModel
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
thesaurusFromApi(thesaurus: any[]): ThesaurusModel {
|
|
48
|
+
thesaurusFromApi(thesaurus: any[], lang3?: string): ThesaurusModel {
|
|
49
49
|
return thesaurus.map((keyword) => {
|
|
50
|
-
|
|
50
|
+
let key = keyword.uri
|
|
51
|
+
// sometines GN can prefix an URI with an "all thesaurus" URI; only keep the last one
|
|
52
|
+
if (key.indexOf('@@@') > -1) {
|
|
53
|
+
key = key.split('@@@')[1]
|
|
54
|
+
}
|
|
55
|
+
const label =
|
|
56
|
+
lang3 && lang3 in keyword.values ? keyword.values[lang3] : keyword.value
|
|
57
|
+
const description =
|
|
58
|
+
lang3 && lang3 in keyword.definitions
|
|
59
|
+
? keyword.definitions[lang3]
|
|
60
|
+
: keyword.definition
|
|
51
61
|
return {
|
|
52
|
-
key
|
|
53
|
-
label
|
|
62
|
+
key,
|
|
63
|
+
label,
|
|
64
|
+
description,
|
|
54
65
|
}
|
|
55
66
|
})
|
|
56
67
|
}
|
|
@@ -14,8 +14,10 @@ import { Organization } from '../../../../../../../libs/common/domain/src/lib/mo
|
|
|
14
14
|
import { Gn4PlatformMapper } from './gn4-platform.mapper'
|
|
15
15
|
import { ltr } from 'semver'
|
|
16
16
|
import { ThesaurusModel } from '../../../../../../../libs/common/domain/src/lib/model/thesaurus/thesaurus.model'
|
|
17
|
+
import { LangService } from '../../../../../../../libs/util/i18n/src'
|
|
17
18
|
|
|
18
19
|
const minApiVersion = '4.2.2'
|
|
20
|
+
|
|
19
21
|
@Injectable()
|
|
20
22
|
export class Gn4PlatformService implements PlatformServiceInterface {
|
|
21
23
|
private readonly type = 'GeoNetwork'
|
|
@@ -50,13 +52,20 @@ export class Gn4PlatformService implements PlatformServiceInterface {
|
|
|
50
52
|
shareReplay(1)
|
|
51
53
|
)
|
|
52
54
|
|
|
55
|
+
/**
|
|
56
|
+
* A map of already loaded thesauri (groups of keywords); the key is a URI
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
private thesauri: Record<string, Observable<ThesaurusModel>> = {}
|
|
60
|
+
|
|
53
61
|
constructor(
|
|
54
62
|
private siteApiService: SiteApiService,
|
|
55
63
|
private meApi: MeApiService,
|
|
56
64
|
private usersApi: UsersApiService,
|
|
57
65
|
private mapper: Gn4PlatformMapper,
|
|
58
66
|
private toolsApiService: ToolsApiService,
|
|
59
|
-
private registriesApiService: RegistriesApiService
|
|
67
|
+
private registriesApiService: RegistriesApiService,
|
|
68
|
+
private langService: LangService
|
|
60
69
|
) {
|
|
61
70
|
this.me$ = this.meApi.getMe().pipe(
|
|
62
71
|
switchMap((apiUser) => this.mapper.userFromMeApi(apiUser)),
|
|
@@ -98,17 +107,47 @@ export class Gn4PlatformService implements PlatformServiceInterface {
|
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
translateKey(key: string): Observable<string> {
|
|
110
|
+
// if the key is a URI, use the registries API to look for the translation
|
|
111
|
+
if (key.match(/^https?:\/\//)) {
|
|
112
|
+
// the thesaurus URI is inferred by removing a part of the keyword URI
|
|
113
|
+
// this is not exact science but it's OK, we'll still end up loading a bunch of keywords at once anyway
|
|
114
|
+
const thesaurusUri = key.replace(/\/([^/]+)$/, '/')
|
|
115
|
+
return this.getThesaurusByUri(thesaurusUri).pipe(
|
|
116
|
+
map((thesaurus) => {
|
|
117
|
+
for (const item of thesaurus) {
|
|
118
|
+
if (item.key === key) return item.label
|
|
119
|
+
}
|
|
120
|
+
return key
|
|
121
|
+
})
|
|
122
|
+
)
|
|
123
|
+
}
|
|
101
124
|
return this.keyTranslations$.pipe(map((translations) => translations[key]))
|
|
102
125
|
}
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.searchKeywords(
|
|
127
|
+
getThesaurusByUri(uri: string): Observable<ThesaurusModel> {
|
|
128
|
+
if (this.thesauri[uri]) {
|
|
129
|
+
return this.thesauri[uri]
|
|
130
|
+
}
|
|
131
|
+
this.thesauri[uri] = this.registriesApiService
|
|
132
|
+
.searchKeywords(
|
|
133
|
+
null,
|
|
134
|
+
this.langService.iso3,
|
|
135
|
+
1000,
|
|
136
|
+
0,
|
|
137
|
+
null,
|
|
138
|
+
null,
|
|
139
|
+
null,
|
|
140
|
+
`${uri}*`
|
|
141
|
+
)
|
|
110
142
|
.pipe(
|
|
111
|
-
map((thesaurus) =>
|
|
143
|
+
map((thesaurus) =>
|
|
144
|
+
this.mapper.thesaurusFromApi(
|
|
145
|
+
thesaurus as any[],
|
|
146
|
+
this.langService.iso3
|
|
147
|
+
)
|
|
148
|
+
),
|
|
149
|
+
shareReplay(1)
|
|
112
150
|
)
|
|
151
|
+
return this.thesauri[uri]
|
|
113
152
|
}
|
|
114
153
|
}
|
|
@@ -48,13 +48,7 @@ export const RecordStatusValues = [
|
|
|
48
48
|
]
|
|
49
49
|
export type RecordStatus = typeof RecordStatusValues[number]
|
|
50
50
|
|
|
51
|
-
export type
|
|
52
|
-
export interface AccessConstraint {
|
|
53
|
-
text: string
|
|
54
|
-
type: AccessConstraintType
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export type License = {
|
|
51
|
+
export type Constraint = {
|
|
58
52
|
text: string
|
|
59
53
|
url?: URL
|
|
60
54
|
}
|
|
@@ -77,10 +71,10 @@ export interface BaseRecord {
|
|
|
77
71
|
kind: RecordKind
|
|
78
72
|
themes: Array<string> // TODO: handle codelists
|
|
79
73
|
keywords: Array<string> // TODO: handle thesaurus and id
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
licenses: Array<Constraint>
|
|
75
|
+
legalConstraints: Array<Constraint>
|
|
76
|
+
securityConstraints: Array<Constraint>
|
|
77
|
+
otherConstraints: Array<Constraint>
|
|
84
78
|
overviews: Array<GraphicOverview>
|
|
85
79
|
extras?: Record<string, unknown>
|
|
86
80
|
landingPage?: URL
|
|
@@ -15,8 +15,5 @@ export abstract class PlatformServiceInterface {
|
|
|
15
15
|
): Observable<UserModel[]>
|
|
16
16
|
abstract getOrganizations(): Observable<Organization[]>
|
|
17
17
|
abstract translateKey(key: string): Observable<string>
|
|
18
|
-
abstract
|
|
19
|
-
thesaurusName: string,
|
|
20
|
-
lang: string
|
|
21
|
-
): Observable<ThesaurusModel>
|
|
18
|
+
abstract getThesaurusByUri(uri: string): Observable<ThesaurusModel>
|
|
22
19
|
}
|
|
@@ -64,7 +64,7 @@ export const GROUPS_FIXTURE = deepFreeze([
|
|
|
64
64
|
defaultCategory: null,
|
|
65
65
|
allowedCategories: [],
|
|
66
66
|
enableAllowedCategories: false,
|
|
67
|
-
id:
|
|
67
|
+
id: 2,
|
|
68
68
|
email: 'ifremer.ifremer@ifremer.admin.fr',
|
|
69
69
|
referrer: null,
|
|
70
70
|
description: "Institut français de recherche pour l'exploitation de la mer",
|
|
@@ -115,7 +115,7 @@ As such, **it is not very interesting at all.**`,
|
|
|
115
115
|
url: new URL('https://data.rennesmetropole.fr/pages/licence/'),
|
|
116
116
|
},
|
|
117
117
|
],
|
|
118
|
-
|
|
118
|
+
legalConstraints: [
|
|
119
119
|
{
|
|
120
120
|
text: "Dataset access isn't possible since it does not really exist",
|
|
121
121
|
type: 'other',
|
|
@@ -125,6 +125,8 @@ As such, **it is not very interesting at all.**`,
|
|
|
125
125
|
type: 'security',
|
|
126
126
|
},
|
|
127
127
|
],
|
|
128
|
+
securityConstraints: [],
|
|
129
|
+
otherConstraints: [],
|
|
128
130
|
spatialExtents: [],
|
|
129
131
|
temporalExtents: [],
|
|
130
132
|
updateFrequency: {
|
|
@@ -189,7 +191,9 @@ Malgré l'attention portée à la création de ces données, il est rappelé que
|
|
|
189
191
|
],
|
|
190
192
|
lineage: `Document d’urbanisme numérisé conformément aux prescriptions nationales du CNIG par le Service d'Information Géographique de l'Agglomération de la Région de Compiègne.
|
|
191
193
|
Ce lot de données produit en 2019, a été numérisé à partir du PCI Vecteur de 2019 et contrôlé par le Service d'Information Géographique de l'Agglomération de la Région de Compiègne.`,
|
|
192
|
-
|
|
194
|
+
legalConstraints: [],
|
|
195
|
+
securityConstraints: [],
|
|
196
|
+
otherConstraints: [],
|
|
193
197
|
useLimitations: ["Aucune condition ne s'applique", 'Licence Ouverte 2.0'],
|
|
194
198
|
licenses: [
|
|
195
199
|
{
|
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
FieldValue,
|
|
5
5
|
FullTextSearchField,
|
|
6
6
|
IsSpatialSearchField,
|
|
7
|
-
KeySearchField,
|
|
8
7
|
LicenseSearchField,
|
|
8
|
+
MultilingualSearchField,
|
|
9
9
|
OrganizationSearchField,
|
|
10
10
|
OwnerSearchField,
|
|
11
11
|
SimpleSearchField,
|
|
12
|
-
|
|
12
|
+
TranslatedSearchField,
|
|
13
13
|
} from './fields'
|
|
14
14
|
import { forkJoin, Observable, of } from 'rxjs'
|
|
15
15
|
import { map } from 'rxjs/operators'
|
|
@@ -21,6 +21,7 @@ export type FieldValues = Record<string, FieldValue[] | FieldValue>
|
|
|
21
21
|
|
|
22
22
|
marker('search.filters.format')
|
|
23
23
|
marker('search.filters.inspireKeyword')
|
|
24
|
+
marker('search.filters.keyword')
|
|
24
25
|
marker('search.filters.isSpatial')
|
|
25
26
|
marker('search.filters.license')
|
|
26
27
|
marker('search.filters.publicationYear')
|
|
@@ -37,29 +38,33 @@ marker('search.filters.contact')
|
|
|
37
38
|
export class FieldsService {
|
|
38
39
|
private fields = {
|
|
39
40
|
publisher: new OrganizationSearchField(this.injector),
|
|
40
|
-
format: new SimpleSearchField('format', 'asc'
|
|
41
|
-
resourceType: new
|
|
42
|
-
|
|
41
|
+
format: new SimpleSearchField('format', this.injector, 'asc'),
|
|
42
|
+
resourceType: new TranslatedSearchField(
|
|
43
|
+
'resourceType',
|
|
44
|
+
this.injector,
|
|
45
|
+
'asc'
|
|
46
|
+
),
|
|
47
|
+
representationType: new TranslatedSearchField(
|
|
43
48
|
'cl_spatialRepresentationType.key',
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
this.injector,
|
|
50
|
+
'asc'
|
|
46
51
|
),
|
|
47
52
|
publicationYear: new SimpleSearchField(
|
|
48
53
|
'publicationYearForResource',
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
this.injector,
|
|
55
|
+
'desc'
|
|
51
56
|
),
|
|
52
|
-
topic: new
|
|
53
|
-
inspireKeyword: new
|
|
57
|
+
topic: new TranslatedSearchField('cl_topic.key', this.injector, 'asc'),
|
|
58
|
+
inspireKeyword: new TranslatedSearchField(
|
|
54
59
|
'th_httpinspireeceuropaeutheme-theme.link',
|
|
55
|
-
|
|
56
|
-
'asc'
|
|
57
|
-
this.injector
|
|
60
|
+
this.injector,
|
|
61
|
+
'asc'
|
|
58
62
|
),
|
|
63
|
+
keyword: new MultilingualSearchField('tag', this.injector, 'desc', 'count'),
|
|
59
64
|
documentStandard: new SimpleSearchField(
|
|
60
65
|
'documentStandard',
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
this.injector,
|
|
67
|
+
'asc'
|
|
63
68
|
),
|
|
64
69
|
isSpatial: new IsSpatialSearchField(this.injector),
|
|
65
70
|
q: new FullTextSearchField(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { firstValueFrom, Observable, of, switchMap
|
|
2
|
-
import {
|
|
1
|
+
import { firstValueFrom, Observable, of, switchMap } from 'rxjs'
|
|
2
|
+
import { map } from 'rxjs/operators'
|
|
3
3
|
import { Injector } from '@angular/core'
|
|
4
4
|
import { TranslateService } from '@ngx-translate/core'
|
|
5
5
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
@@ -13,7 +13,10 @@ import {
|
|
|
13
13
|
FieldFilters,
|
|
14
14
|
TermBucket,
|
|
15
15
|
} from '../../../../../../../libs/common/domain/src/lib/model/search'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
ElasticsearchService,
|
|
18
|
+
METADATA_LANGUAGE,
|
|
19
|
+
} from '../../../../../../../libs/api/repository/src'
|
|
17
20
|
import { LangService } from '../../../../../../../libs/util/i18n/src'
|
|
18
21
|
|
|
19
22
|
export type FieldValue = string | number
|
|
@@ -36,8 +39,9 @@ export class SimpleSearchField implements AbstractSearchField {
|
|
|
36
39
|
|
|
37
40
|
constructor(
|
|
38
41
|
protected esFieldName: string,
|
|
42
|
+
protected injector: Injector,
|
|
39
43
|
protected order: 'asc' | 'desc' = 'asc',
|
|
40
|
-
protected
|
|
44
|
+
protected orderType: 'key' | 'count' = 'key'
|
|
41
45
|
) {}
|
|
42
46
|
|
|
43
47
|
protected getAggregations(): AggregationsParams {
|
|
@@ -46,13 +50,13 @@ export class SimpleSearchField implements AbstractSearchField {
|
|
|
46
50
|
type: 'terms',
|
|
47
51
|
field: this.esFieldName,
|
|
48
52
|
limit: 1000,
|
|
49
|
-
sort: [this.order,
|
|
53
|
+
sort: [this.order, this.orderType],
|
|
50
54
|
},
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
protected async getBucketLabel(bucket: TermBucket): Promise<string> {
|
|
55
|
-
return bucket.term
|
|
59
|
+
return bucket.term.toString()
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
getAvailableValues(): Observable<FieldAvailableValue[]> {
|
|
@@ -88,9 +92,18 @@ export class SimpleSearchField implements AbstractSearchField {
|
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
export class
|
|
95
|
+
export class TranslatedSearchField extends SimpleSearchField {
|
|
92
96
|
protected platformService = this.injector.get(PlatformServiceInterface)
|
|
93
97
|
|
|
98
|
+
constructor(
|
|
99
|
+
protected esFieldName: string,
|
|
100
|
+
protected injector: Injector,
|
|
101
|
+
protected order: 'asc' | 'desc' = 'asc',
|
|
102
|
+
protected orderType: 'key' | 'count' = 'key'
|
|
103
|
+
) {
|
|
104
|
+
super(esFieldName, injector, order, orderType)
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
protected async getTranslation(key: string) {
|
|
95
108
|
return firstValueFrom(this.platformService.translateKey(key))
|
|
96
109
|
}
|
|
@@ -100,6 +113,7 @@ export class KeySearchField extends SimpleSearchField {
|
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
getAvailableValues(): Observable<FieldAvailableValue[]> {
|
|
116
|
+
if (this.orderType === 'count') return super.getAvailableValues()
|
|
103
117
|
// sort values by alphabetical order
|
|
104
118
|
return super
|
|
105
119
|
.getAvailableValues()
|
|
@@ -111,34 +125,29 @@ export class KeySearchField extends SimpleSearchField {
|
|
|
111
125
|
}
|
|
112
126
|
}
|
|
113
127
|
|
|
114
|
-
|
|
128
|
+
/**
|
|
129
|
+
* This search field will either target the `.default` field, or a specific `.langxyz` field according
|
|
130
|
+
* to the defined METADATA_LANGUAGE token
|
|
131
|
+
* The provided ES field name should not include any prefix such as `.langeng`
|
|
132
|
+
*/
|
|
133
|
+
export class MultilingualSearchField extends SimpleSearchField {
|
|
115
134
|
private langService = this.injector.get(LangService)
|
|
116
|
-
private
|
|
117
|
-
.getThesaurusByLang(this.thesaurusName, this.langService.iso3)
|
|
118
|
-
.pipe(
|
|
119
|
-
catchError(() => {
|
|
120
|
-
console.warn('Error while loading thesaurus language package')
|
|
121
|
-
return of([])
|
|
122
|
-
}),
|
|
123
|
-
shareReplay(1)
|
|
124
|
-
)
|
|
135
|
+
private searchLanguage = this.injector.get(METADATA_LANGUAGE, null)
|
|
125
136
|
|
|
126
137
|
constructor(
|
|
127
|
-
esFieldName: string,
|
|
128
|
-
protected
|
|
129
|
-
order: 'asc' | 'desc' = 'asc',
|
|
130
|
-
|
|
138
|
+
protected esFieldName: string,
|
|
139
|
+
protected injector: Injector,
|
|
140
|
+
protected order: 'asc' | 'desc' = 'asc',
|
|
141
|
+
protected orderType: 'key' | 'count' = 'key'
|
|
131
142
|
) {
|
|
132
|
-
super(esFieldName, order,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
)
|
|
143
|
+
super(esFieldName, injector, order, orderType)
|
|
144
|
+
// note: we're excluding the metadata language "current" value because that would produce
|
|
145
|
+
// permalinks that might not work for different users
|
|
146
|
+
if (this.searchLanguage && this.searchLanguage !== 'current') {
|
|
147
|
+
this.esFieldName += `.lang${this.searchLanguage}`
|
|
148
|
+
} else {
|
|
149
|
+
this.esFieldName += '.default'
|
|
150
|
+
}
|
|
142
151
|
}
|
|
143
152
|
}
|
|
144
153
|
|
|
@@ -163,7 +172,7 @@ export class IsSpatialSearchField extends SimpleSearchField {
|
|
|
163
172
|
private translateService = this.injector.get(TranslateService)
|
|
164
173
|
|
|
165
174
|
constructor(injector: Injector) {
|
|
166
|
-
super('isSpatial', 'asc'
|
|
175
|
+
super('isSpatial', injector, 'asc')
|
|
167
176
|
this.esService.registerRuntimeField(
|
|
168
177
|
'isSpatial',
|
|
169
178
|
`String result = 'no';
|
|
@@ -223,7 +232,7 @@ export class LicenseSearchField extends SimpleSearchField {
|
|
|
223
232
|
private translateService = this.injector.get(TranslateService)
|
|
224
233
|
|
|
225
234
|
constructor(injector: Injector) {
|
|
226
|
-
super('license', 'asc'
|
|
235
|
+
super('license', injector, 'asc')
|
|
227
236
|
this.esService.registerRuntimeField(
|
|
228
237
|
'license',
|
|
229
238
|
`String raw = '';
|
|
@@ -330,7 +339,7 @@ export class OrganizationSearchField implements AbstractSearchField {
|
|
|
330
339
|
}
|
|
331
340
|
export class OwnerSearchField extends SimpleSearchField {
|
|
332
341
|
constructor(injector: Injector) {
|
|
333
|
-
super('owner', 'asc'
|
|
342
|
+
super('owner', injector, 'asc')
|
|
334
343
|
}
|
|
335
344
|
|
|
336
345
|
getAvailableValues(): Observable<FieldAvailableValue[]> {
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
margin-top: 0;
|
|
75
75
|
margin-bottom: 10px;
|
|
76
76
|
color: var(--color-primary) !important;
|
|
77
|
-
text-decoration: none
|
|
78
|
-
@apply font-bold;
|
|
77
|
+
text-decoration: none;
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
:host ::ng-deep .markdown-body p > a:hover {
|
|
82
81
|
color: var(--color-primary-darker) !important;
|
|
82
|
+
@apply underline;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/** Blockquotes **/
|
|
@@ -10,6 +10,50 @@
|
|
|
10
10
|
</gn-ui-content-ghost>
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
|
+
<gn-ui-expandable-panel [title]="'record.metadata.usage' | translate">
|
|
14
|
+
<div class="flex flex-col gap-[10px] mr-4 py-[12px] rounded text-gray-900">
|
|
15
|
+
<ng-container *ngFor="let license of licenses">
|
|
16
|
+
<div *ngIf="license.url; else noUrl" class="text-primary">
|
|
17
|
+
<a
|
|
18
|
+
[href]="license.url"
|
|
19
|
+
target="_blank"
|
|
20
|
+
class="cursor-pointer hover:underline transition-all"
|
|
21
|
+
>
|
|
22
|
+
{{ license.text }}
|
|
23
|
+
<mat-icon
|
|
24
|
+
class="material-symbols-outlined !w-[12px] !h-[12px] !text-[12px] opacity-75 shrink-0"
|
|
25
|
+
>open_in_new</mat-icon
|
|
26
|
+
>
|
|
27
|
+
</a>
|
|
28
|
+
</div>
|
|
29
|
+
<ng-template #noUrl>
|
|
30
|
+
<div class="text-primary" gnUiLinkify>
|
|
31
|
+
{{ license.text }}
|
|
32
|
+
</div>
|
|
33
|
+
</ng-template>
|
|
34
|
+
</ng-container>
|
|
35
|
+
<ng-container *ngIf="legalConstraints.length">
|
|
36
|
+
<gn-ui-markdown-parser
|
|
37
|
+
*ngFor="let constraint of legalConstraints"
|
|
38
|
+
[textContent]="constraint"
|
|
39
|
+
>
|
|
40
|
+
</gn-ui-markdown-parser>
|
|
41
|
+
</ng-container>
|
|
42
|
+
<ng-container *ngIf="otherConstraints.length">
|
|
43
|
+
<div gnUiLinkify *ngFor="let constraint of otherConstraints">
|
|
44
|
+
<h5 translate class="font-medium text-black text-sm mb-[2px] mt-[16px]">
|
|
45
|
+
record.metadata.otherConstraints
|
|
46
|
+
</h5>
|
|
47
|
+
<gn-ui-markdown-parser [textContent]="constraint">
|
|
48
|
+
</gn-ui-markdown-parser>
|
|
49
|
+
</div>
|
|
50
|
+
</ng-container>
|
|
51
|
+
|
|
52
|
+
<span class="noUsage" *ngIf="!hasUsage">
|
|
53
|
+
{{ 'record.metadata.noUsage' | translate }}
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
</gn-ui-expandable-panel>
|
|
13
57
|
<gn-ui-expandable-panel
|
|
14
58
|
class="metadata-origin"
|
|
15
59
|
*ngIf="
|
|
@@ -22,7 +66,7 @@
|
|
|
22
66
|
>
|
|
23
67
|
<p
|
|
24
68
|
*ngIf="metadata.lineage"
|
|
25
|
-
class="mb-5 pt-4 whitespace-pre-line break-words"
|
|
69
|
+
class="mb-5 pt-4 whitespace-pre-line break-words text-gray-900"
|
|
26
70
|
gnUiLinkify
|
|
27
71
|
>
|
|
28
72
|
{{ metadata.lineage }}
|
|
@@ -54,23 +98,6 @@
|
|
|
54
98
|
</div>
|
|
55
99
|
</div>
|
|
56
100
|
</gn-ui-expandable-panel>
|
|
57
|
-
<gn-ui-expandable-panel [title]="'record.metadata.usage' | translate">
|
|
58
|
-
<div class="py-4 px-6 rounded bg-gray-100 text-gray-700 flex flex-wrap gap-2">
|
|
59
|
-
<gn-ui-badge *ngIf="metadata.extras?.isOpenData">
|
|
60
|
-
<span translate>record.metadata.isOpenData</span>
|
|
61
|
-
</gn-ui-badge>
|
|
62
|
-
<span
|
|
63
|
-
class="text-primary font-medium"
|
|
64
|
-
*ngFor="let usage of usages"
|
|
65
|
-
gnUiLinkify
|
|
66
|
-
>
|
|
67
|
-
{{ usage }}
|
|
68
|
-
</span>
|
|
69
|
-
<span class="text-primary font-medium noUsage" *ngIf="!hasUsage">
|
|
70
|
-
{{ 'record.metadata.noUsage' | translate }}
|
|
71
|
-
</span>
|
|
72
|
-
</div>
|
|
73
|
-
</gn-ui-expandable-panel>
|
|
74
101
|
<gn-ui-expandable-panel
|
|
75
102
|
*ngIf="metadata.landingPage"
|
|
76
103
|
[title]="'record.metadata.details' | translate"
|
|
@@ -22,18 +22,42 @@ export class MetadataInfoComponent {
|
|
|
22
22
|
get hasUsage() {
|
|
23
23
|
return (
|
|
24
24
|
this.metadata.extras?.isOpenData === true ||
|
|
25
|
-
this.metadata.
|
|
26
|
-
|
|
25
|
+
(this.metadata.legalConstraints?.length > 0 &&
|
|
26
|
+
this.legalConstraints.length > 0) ||
|
|
27
|
+
(this.metadata.otherConstraints?.length > 0 &&
|
|
28
|
+
this.otherConstraints.length > 0) ||
|
|
29
|
+
(this.metadata.licenses?.length > 0 && this.licenses.length > 0)
|
|
27
30
|
)
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
get
|
|
33
|
+
get legalConstraints() {
|
|
31
34
|
let array = []
|
|
32
|
-
if (this.metadata.
|
|
33
|
-
array = array.concat(
|
|
35
|
+
if (this.metadata.legalConstraints?.length) {
|
|
36
|
+
array = array.concat(
|
|
37
|
+
this.metadata.legalConstraints.filter((c) => c.text).map((c) => c.text)
|
|
38
|
+
)
|
|
34
39
|
}
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
return array
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get otherConstraints() {
|
|
44
|
+
let array = []
|
|
45
|
+
if (this.metadata.otherConstraints?.length) {
|
|
46
|
+
array = array.concat(
|
|
47
|
+
this.metadata.otherConstraints.filter((c) => c.text).map((c) => c.text)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
return array
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get licenses(): { text: string; url: string }[] {
|
|
54
|
+
let array = []
|
|
55
|
+
if (this.metadata.licenses?.length) {
|
|
56
|
+
array = array.concat(
|
|
57
|
+
this.metadata.licenses
|
|
58
|
+
.filter((c) => c.text)
|
|
59
|
+
.map((c) => ({ text: c.text, url: c.url }))
|
|
60
|
+
)
|
|
37
61
|
}
|
|
38
62
|
return array
|
|
39
63
|
}
|
|
@@ -56,9 +80,4 @@ export class MetadataInfoComponent {
|
|
|
56
80
|
onKeywordClick(keyword: string) {
|
|
57
81
|
this.keyword.emit(keyword)
|
|
58
82
|
}
|
|
59
|
-
|
|
60
|
-
copyText() {
|
|
61
|
-
navigator.clipboard.writeText(this.metadata.uniqueIdentifier)
|
|
62
|
-
;(event.target as HTMLElement).blur()
|
|
63
|
-
}
|
|
64
83
|
}
|
|
@@ -32,8 +32,7 @@ export class MetadataQualityComponent implements OnChanges {
|
|
|
32
32
|
|
|
33
33
|
get calculatedQualityScore(): number {
|
|
34
34
|
return Math.round(
|
|
35
|
-
(this.items.filter(({ value }) => value
|
|
36
|
-
this.items.length
|
|
35
|
+
(this.items.filter(({ value }) => value).length * 100) / this.items.length
|
|
37
36
|
)
|
|
38
37
|
}
|
|
39
38
|
|