geonetwork-ui 2.2.0-dev.95b40cd8 → 2.2.0-dev.9d7dfa2e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/gn4/atomic-operations.mjs +27 -1
  2. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +23 -11
  3. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.mjs +4 -2
  4. package/esm2022/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.mjs +1 -1
  5. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.mjs +14 -0
  6. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/converter.mjs +12 -9
  7. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +33 -8
  8. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +31 -14
  9. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.mjs +2 -2
  10. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
  11. package/esm2022/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.mjs +13 -13
  12. package/esm2022/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.mjs +3 -3
  13. package/esm2022/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.mjs +3 -3
  14. package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +3 -3
  15. package/esm2022/libs/ui/elements/src/lib/max-lines/max-lines.component.mjs +5 -3
  16. package/esm2022/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.mjs +3 -3
  17. package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +19 -7
  18. package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +2 -2
  19. package/esm2022/libs/ui/elements/src/lib/thumbnail/thumbnail.component.mjs +3 -3
  20. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +4 -4
  21. package/esm2022/libs/ui/inputs/src/lib/button/button.component.mjs +3 -3
  22. package/esm2022/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.mjs +3 -3
  23. package/esm2022/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.mjs +3 -3
  24. package/esm2022/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.mjs +3 -3
  25. package/esm2022/libs/ui/inputs/src/lib/text-area/text-area.component.mjs +3 -3
  26. package/esm2022/libs/ui/inputs/src/lib/ui-inputs.module.mjs +6 -6
  27. package/esm2022/libs/ui/layout/src/lib/carousel/carousel.component.mjs +3 -3
  28. package/esm2022/libs/util/i18n/src/lib/i18n.constants.mjs +18 -1
  29. package/esm2022/libs/util/shared/src/lib/utils/index.mjs +2 -1
  30. package/esm2022/libs/util/shared/src/lib/utils/temporal-extent-union.mjs +32 -0
  31. package/esm2022/translations/de.json +34 -27
  32. package/esm2022/translations/en.json +26 -19
  33. package/esm2022/translations/es.json +23 -16
  34. package/esm2022/translations/fr.json +44 -37
  35. package/esm2022/translations/it.json +38 -31
  36. package/esm2022/translations/nl.json +24 -17
  37. package/esm2022/translations/pt.json +23 -16
  38. package/fesm2022/geonetwork-ui.mjs +465 -258
  39. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  40. package/libs/api/metadata-converter/src/lib/gn4/atomic-operations.d.ts +2 -0
  41. package/libs/api/metadata-converter/src/lib/gn4/atomic-operations.d.ts.map +1 -1
  42. package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
  43. package/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.d.ts.map +1 -1
  44. package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts +4 -5
  45. package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts.map +1 -1
  46. package/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.d.ts +3 -0
  47. package/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.d.ts.map +1 -0
  48. package/libs/api/metadata-converter/src/lib/iso19139/converter.d.ts.map +1 -1
  49. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +3 -3
  50. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
  51. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +1 -1
  52. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  53. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts.map +1 -1
  54. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +16 -2
  55. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  56. package/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.d.ts +1 -1
  57. package/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.d.ts.map +1 -1
  58. package/libs/ui/elements/src/lib/max-lines/max-lines.component.d.ts.map +1 -1
  59. package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts +9 -3
  60. package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts.map +1 -1
  61. package/libs/ui/elements/src/lib/ui-elements.module.d.ts +13 -13
  62. package/libs/ui/inputs/src/lib/button/button.component.d.ts +1 -1
  63. package/libs/ui/inputs/src/lib/button/button.component.d.ts.map +1 -1
  64. package/libs/ui/inputs/src/lib/text-area/text-area.component.d.ts +1 -1
  65. package/libs/ui/inputs/src/lib/text-area/text-area.component.d.ts.map +1 -1
  66. package/libs/ui/inputs/src/lib/ui-inputs.module.d.ts +40 -40
  67. package/libs/ui/layout/src/lib/interactive-table/interactive-table-column/interactive-table-column.component.d.ts +1 -1
  68. package/libs/util/i18n/src/lib/i18n.constants.d.ts +17 -0
  69. package/libs/util/i18n/src/lib/i18n.constants.d.ts.map +1 -1
  70. package/libs/util/shared/src/lib/utils/index.d.ts +1 -0
  71. package/libs/util/shared/src/lib/utils/index.d.ts.map +1 -1
  72. package/libs/util/shared/src/lib/utils/temporal-extent-union.d.ts +5 -0
  73. package/libs/util/shared/src/lib/utils/temporal-extent-union.d.ts.map +1 -0
  74. package/package.json +1 -1
  75. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +57 -2
  76. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +52 -6
  77. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +207 -20
  78. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +29 -0
  79. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +30 -11
  80. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.ts +3 -1
  81. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +4 -5
  82. package/src/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.ts +16 -0
  83. package/src/libs/api/metadata-converter/src/lib/iso19139/converter.ts +11 -9
  84. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +58 -20
  85. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +86 -38
  86. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +1 -1
  87. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +20 -3
  88. package/src/libs/common/fixtures/src/lib/elasticsearch/full-response.ts +1 -1
  89. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +46 -8
  90. package/src/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.ts +12 -12
  91. package/src/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.html +3 -4
  92. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html +1 -1
  93. package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.css +5 -0
  94. package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.html +39 -0
  95. package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.ts +58 -0
  96. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +1 -0
  97. package/src/libs/ui/elements/src/lib/max-lines/max-lines.component.html +5 -1
  98. package/src/libs/ui/elements/src/lib/max-lines/max-lines.component.ts +1 -0
  99. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +1 -1
  100. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +113 -25
  101. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +20 -3
  102. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +1 -1
  103. package/src/libs/ui/elements/src/lib/thumbnail/thumbnail.component.html +2 -2
  104. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +1 -1
  105. package/src/libs/ui/inputs/src/lib/button/button.component.ts +1 -0
  106. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  107. package/src/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.html +3 -1
  108. package/src/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.css +3 -0
  109. package/src/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.html +1 -1
  110. package/src/libs/ui/inputs/src/lib/text-area/text-area.component.ts +1 -0
  111. package/src/libs/ui/inputs/src/lib/ui-inputs.module.ts +2 -2
  112. package/src/libs/ui/layout/src/lib/carousel/carousel.component.html +1 -0
  113. package/src/libs/util/i18n/src/lib/i18n.constants.ts +18 -0
  114. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  115. package/src/libs/util/shared/src/lib/utils/temporal-extent-union.ts +32 -0
  116. package/tailwind.base.css +2 -2
  117. package/translations/de.json +34 -27
  118. package/translations/en.json +26 -19
  119. package/translations/es.json +23 -16
  120. package/translations/fr.json +44 -37
  121. package/translations/it.json +38 -31
  122. package/translations/nl.json +24 -17
  123. package/translations/pt.json +23 -16
  124. package/translations/sk.json +24 -17
@@ -29,7 +29,7 @@ import {
29
29
  writeSecurityConstraints,
30
30
  writeSpatialRepresentation,
31
31
  writeStatus,
32
- writeThemes,
32
+ writeTopics,
33
33
  writeTitle,
34
34
  writeUniqueIdentifier,
35
35
  writeUpdateFrequency,
@@ -51,12 +51,12 @@ import {
51
51
  readOverviews,
52
52
  readOwnerOrganization,
53
53
  readRecordUpdated,
54
+ readRecordPublished,
54
55
  readSecurityConstraints,
55
56
  readSpatialExtents,
56
57
  readSpatialRepresentation,
57
58
  readStatus,
58
59
  readTemporalExtents,
59
- readThemes,
60
60
  readTitle,
61
61
  readUniqueIdentifier,
62
62
  readUpdateFrequency,
@@ -75,17 +75,15 @@ export function toModel(xml: string): CatalogRecord {
75
75
  const contacts = readContacts(rootEl)
76
76
  const recordUpdated = readRecordUpdated(rootEl)
77
77
  const recordCreated = recordUpdated
78
+ const recordPublished = readRecordPublished(rootEl)
78
79
  const keywords = readKeywords(rootEl)
79
- const themes = readThemes(rootEl)
80
+ const topics = readIsoTopics(rootEl)
80
81
  const legalConstraints = readLegalConstraints(rootEl)
81
82
  const otherConstraints = readOtherConstraints(rootEl)
82
83
  const securityConstraints = readSecurityConstraints(rootEl)
83
84
  const licenses = readLicenses(rootEl)
84
85
  const overviews = readOverviews(rootEl)
85
86
 
86
- // not used yet
87
- const isoTopics = readIsoTopics(rootEl)
88
-
89
87
  if (kind === 'dataset') {
90
88
  const status = readStatus(rootEl)
91
89
  const datasetCreated = readDatasetCreated(rootEl)
@@ -100,8 +98,10 @@ export function toModel(xml: string): CatalogRecord {
100
98
  return {
101
99
  uniqueIdentifier,
102
100
  kind,
101
+ languages: [],
103
102
  recordCreated,
104
103
  recordUpdated,
104
+ recordPublished,
105
105
  status,
106
106
  title,
107
107
  abstract,
@@ -109,7 +109,7 @@ export function toModel(xml: string): CatalogRecord {
109
109
  contacts,
110
110
  contactsForResource: [], // FIXME: is that really useful??
111
111
  keywords,
112
- themes,
112
+ topics,
113
113
  licenses,
114
114
  legalConstraints,
115
115
  securityConstraints,
@@ -129,14 +129,16 @@ export function toModel(xml: string): CatalogRecord {
129
129
  return {
130
130
  uniqueIdentifier,
131
131
  kind,
132
+ languages: [],
132
133
  recordCreated,
133
134
  recordUpdated,
135
+ recordPublished,
134
136
  title,
135
137
  abstract,
136
138
  ownerOrganization,
137
139
  contacts,
138
140
  keywords,
139
- themes,
141
+ topics,
140
142
  licenses,
141
143
  legalConstraints,
142
144
  securityConstraints,
@@ -168,7 +170,7 @@ export function toXml(record: CatalogRecord, originalXml?: string): string {
168
170
  writeAbstract(record, rootEl)
169
171
  fieldChanged('contacts') && writeContacts(record, rootEl)
170
172
  fieldChanged('keywords') && writeKeywords(record, rootEl)
171
- fieldChanged('themes') && writeThemes(record, rootEl)
173
+ fieldChanged('topics') && writeTopics(record, rootEl)
172
174
  fieldChanged('legalConstraints') && writeLegalConstraints(record, rootEl)
173
175
  fieldChanged('securityConstraints') &&
174
176
  writeSecurityConstraints(record, rootEl)
@@ -5,6 +5,8 @@ import {
5
5
  DatasetTemporalExtent,
6
6
  GraphicOverview,
7
7
  Individual,
8
+ Keyword,
9
+ KeywordThesaurus,
8
10
  Organization,
9
11
  RecordKind,
10
12
  RecordStatus,
@@ -39,6 +41,7 @@ import {
39
41
  } from '../function-utils'
40
42
  import { getRoleFromRoleCode } from './codelists/role.mapper'
41
43
  import { matchMimeType, matchProtocol } from '../common/distribution.mapper'
44
+ import { getKeywordTypeFromKeywordTypeCode } from './codelists/keyword.mapper'
42
45
 
43
46
  function extractCharacterString(): ChainableFunction<XmlElement, string> {
44
47
  return pipe(
@@ -447,7 +450,7 @@ function findIdentification() {
447
450
  }
448
451
  }
449
452
 
450
- function extractCitationDate(type: 'creation' | 'revision') {
453
+ function extractCitationDate(type: 'creation' | 'revision' | 'publication') {
451
454
  return pipe(
452
455
  findIdentification(),
453
456
  findNestedElements('gmd:citation', 'gmd:CI_Citation', 'gmd:date'),
@@ -514,6 +517,10 @@ export function readRecordUpdated(rootEl: XmlElement): Date {
514
517
  return pipe(findChildElement('gmd:dateStamp'), extractDateTime())(rootEl)
515
518
  }
516
519
 
520
+ export function readRecordPublished(rootEl: XmlElement): Date {
521
+ return pipe(findChildElement('gmd:dateStamp'), extractDateTime())(rootEl)
522
+ }
523
+
517
524
  export function readTitle(rootEl: XmlElement): string {
518
525
  return pipe(
519
526
  findIdentification(),
@@ -552,29 +559,60 @@ export function readContacts(rootEl: XmlElement): Individual[] {
552
559
  )(rootEl)
553
560
  }
554
561
 
555
- function readKeywordsOfType(isTheme: boolean) {
556
- return pipe(
557
- findIdentification(),
558
- findNestedElements('gmd:descriptiveKeywords', 'gmd:MD_Keywords'),
559
- filterArray(
560
- pipe(
561
- findChildrenElement('gmd:MD_KeywordTypeCode'),
562
- mapArray(readAttribute('codeListValue')),
563
- map((values) => isTheme === values.indexOf('theme') > -1)
564
- )
565
- ),
566
- mapArray(findChildrenElement('gmd:keyword')),
567
- flattenArray(),
568
- mapArray(extractCharacterString())
562
+ // from gmd:thesaurusName
563
+ function readThesaurus(rootEl: XmlElement): KeywordThesaurus {
564
+ if (!rootEl) return null
565
+
566
+ const findIdentifier = findNestedElement(
567
+ 'gmd:CI_Citation',
568
+ 'gmd:identifier',
569
+ 'gmd:MD_Identifier',
570
+ 'gmd:code'
569
571
  )
572
+ const id = pipe(findIdentifier, extractCharacterString())(rootEl)
573
+ const url = pipe(findIdentifier, extractUrl())(rootEl)
574
+ const name = pipe(
575
+ findNestedElement('gmd:CI_Citation', 'gmd:title'),
576
+ extractCharacterString()
577
+ )(rootEl)
578
+ return {
579
+ id,
580
+ ...(name && { name }),
581
+ ...(url && { url }),
582
+ }
570
583
  }
571
584
 
572
- export function readKeywords(rootEl: XmlElement): string[] {
573
- return readKeywordsOfType(false)(rootEl)
585
+ // from gmd:MD_Keywords
586
+ function readKeywordGroup(rootEl: XmlElement): Keyword[] {
587
+ const type = pipe(
588
+ findChildrenElement('gmd:MD_KeywordTypeCode'),
589
+ mapArray(readAttribute('codeListValue')),
590
+ map((values) => getKeywordTypeFromKeywordTypeCode(values[0]))
591
+ )(rootEl)
592
+ const thesaurus = pipe(
593
+ findNestedElement('gmd:thesaurusName'),
594
+ readThesaurus
595
+ )(rootEl)
596
+ return pipe(
597
+ findChildrenElement('gmd:keyword'),
598
+ mapArray((el) => {
599
+ const label = extractCharacterString()(el)
600
+ return {
601
+ ...(thesaurus ? { thesaurus } : {}),
602
+ label,
603
+ type,
604
+ } as Keyword
605
+ })
606
+ )(rootEl)
574
607
  }
575
608
 
576
- export function readThemes(rootEl: XmlElement): string[] {
577
- return readKeywordsOfType(true)(rootEl)
609
+ export function readKeywords(rootEl: XmlElement): Keyword[] {
610
+ return pipe(
611
+ findIdentification(),
612
+ findNestedElements('gmd:descriptiveKeywords', 'gmd:MD_Keywords'),
613
+ mapArray(readKeywordGroup),
614
+ flattenArray()
615
+ )(rootEl)
578
616
  }
579
617
 
580
618
  export function readStatus(rootEl: XmlElement): RecordStatus {
@@ -627,7 +665,7 @@ export function readLicenses(rootEl: XmlElement): Constraint[] {
627
665
  export function readIsoTopics(rootEl: XmlElement): string[] {
628
666
  return pipe(
629
667
  findIdentification(),
630
- findChildrenElement('gmd:MD_TopicCategoryCode', false),
668
+ findChildrenElement('gmd:MD_TopicCategoryCode', true),
631
669
  mapArray(readText())
632
670
  )(rootEl)
633
671
  }
@@ -5,6 +5,8 @@ import {
5
5
  DatasetRecord,
6
6
  DatasetServiceDistribution,
7
7
  Individual,
8
+ Keyword,
9
+ KeywordThesaurus,
8
10
  RecordStatus,
9
11
  Role,
10
12
  ServiceEndpoint,
@@ -25,6 +27,7 @@ import {
25
27
  findNestedElement,
26
28
  findNestedElements,
27
29
  readAttribute,
30
+ readText,
28
31
  removeAllChildren,
29
32
  removeChildren,
30
33
  removeChildrenByName,
@@ -288,7 +291,10 @@ function appendResponsibleParty(contact: Individual) {
288
291
  )
289
292
  }
290
293
 
291
- function updateCitationDate(date: Date, type: 'revision' | 'creation') {
294
+ function updateCitationDate(
295
+ date: Date,
296
+ type: 'revision' | 'creation' | 'publication'
297
+ ) {
292
298
  return pipe(
293
299
  findNestedElements('gmd:date', 'gmd:CI_Date'),
294
300
  filterArray(
@@ -305,7 +311,10 @@ function updateCitationDate(date: Date, type: 'revision' | 'creation') {
305
311
  )
306
312
  }
307
313
 
308
- function appendCitationDate(date, type: 'revision' | 'creation') {
314
+ function appendCitationDate(
315
+ date,
316
+ type: 'revision' | 'creation' | 'publication'
317
+ ) {
309
318
  return appendChildren(
310
319
  pipe(
311
320
  createElement('gmd:date'),
@@ -326,47 +335,78 @@ function appendCitationDate(date, type: 'revision' | 'creation') {
326
335
  )
327
336
  }
328
337
 
329
- function removeKeywords(type: string | null) {
330
- return removeChildren(
331
- pipe(
332
- findNestedElements('gmd:descriptiveKeywords'),
333
- filterArray(
334
- pipe(
335
- findNestedElement(
336
- 'gmd:MD_Keywords',
337
- 'gmd:type',
338
- 'gmd:MD_KeywordTypeCode'
339
- ),
340
- readAttribute('codeListValue'),
341
- // if a specific type is targeted, compare with it; otherwise remove keywords if they have no type defined
342
- map((typeValue) => (type !== null ? type === typeValue : true))
338
+ function removeKeywords() {
339
+ return removeChildren(pipe(findNestedElements('gmd:descriptiveKeywords')))
340
+ }
341
+
342
+ // returns a <gmd:thesaurusName> element
343
+ function createThesaurus(thesaurus: KeywordThesaurus) {
344
+ return pipe(
345
+ createElement('gmd:thesaurusName'),
346
+ createChild('gmd:CI_Citation'),
347
+ appendChildren(
348
+ pipe(
349
+ createElement('gmd:title'),
350
+ writeCharacterString(thesaurus.name || thesaurus.id)
351
+ ),
352
+ pipe(
353
+ createElement('gmd:identifier'),
354
+ createChild('gmd:MD_Identifier'),
355
+ appendChildren(
356
+ pipe(
357
+ createElement('gmd:code'),
358
+ thesaurus.url
359
+ ? writeAnchor(thesaurus.url, thesaurus.id)
360
+ : writeCharacterString(thesaurus.id)
361
+ )
343
362
  )
344
363
  )
345
364
  )
346
365
  )
347
366
  }
348
367
 
349
- function appendKeywords(keywords: string[], type: string | null) {
368
+ function appendKeywords(keywords: Keyword[]) {
369
+ const keywordsByThesaurus: Keyword[][] = keywords.reduce((acc, keyword) => {
370
+ const thesaurusId = keyword.thesaurus?.id
371
+ const type = keyword.type
372
+ let existingGroup = acc.find((group) =>
373
+ group[0].thesaurus
374
+ ? group[0].thesaurus.id === thesaurusId
375
+ : group[0].type === type
376
+ )
377
+ if (!existingGroup) {
378
+ existingGroup = []
379
+ acc.push(existingGroup)
380
+ }
381
+ existingGroup.push(keyword)
382
+ return acc
383
+ }, [])
350
384
  return appendChildren(
351
- pipe(
352
- createElement('gmd:descriptiveKeywords'),
353
- createChild('gmd:MD_Keywords'),
354
- type !== null
355
- ? appendChildren(
385
+ ...keywordsByThesaurus.map((keywords) =>
386
+ pipe(
387
+ createElement('gmd:descriptiveKeywords'),
388
+ createChild('gmd:MD_Keywords'),
389
+ appendChildren(
390
+ pipe(
391
+ createElement('gmd:type'),
392
+ createChild('gmd:MD_KeywordTypeCode'),
393
+ addAttribute(
394
+ 'codeList',
395
+ 'http://standards.iso.org/iso/19139/resources/gmxCodelists.xml#MD_KeywordTypeCode'
396
+ ),
397
+ addAttribute('codeListValue', keywords[0].type)
398
+ )
399
+ ),
400
+ keywords[0].thesaurus
401
+ ? appendChildren(createThesaurus(keywords[0].thesaurus))
402
+ : noop,
403
+ appendChildren(
404
+ ...keywords.map((keyword) =>
356
405
  pipe(
357
- createElement('gmd:type'),
358
- createChild('gmd:MD_KeywordTypeCode'),
359
- addAttribute(
360
- 'codeList',
361
- 'http://standards.iso.org/iso/19139/resources/gmxCodelists.xml#MD_KeywordTypeCode'
362
- ),
363
- addAttribute('codeListValue', type)
406
+ createElement('gmd:keyword'),
407
+ writeCharacterString(keyword.label)
364
408
  )
365
409
  )
366
- : noop,
367
- appendChildren(
368
- ...keywords.map((keyword) =>
369
- pipe(createElement('gmd:keyword'), writeCharacterString(keyword))
370
410
  )
371
411
  )
372
412
  )
@@ -740,16 +780,24 @@ export function writeContacts(record: CatalogRecord, rootEl: XmlElement) {
740
780
  export function writeKeywords(record: CatalogRecord, rootEl: XmlElement) {
741
781
  pipe(
742
782
  findOrCreateIdentification(),
743
- removeKeywords(null),
744
- appendKeywords(record.keywords, null)
783
+ removeKeywords(),
784
+ appendKeywords(record.keywords)
745
785
  )(rootEl)
746
786
  }
747
787
 
748
- export function writeThemes(record: CatalogRecord, rootEl: XmlElement) {
788
+ export function writeTopics(record: CatalogRecord, rootEl: XmlElement) {
749
789
  pipe(
750
790
  findOrCreateIdentification(),
751
- removeKeywords('theme'),
752
- appendKeywords(record.themes, 'theme')
791
+ removeChildrenByName('gmd:topicCategory'),
792
+ appendChildren(
793
+ ...record.topics.map((topic) =>
794
+ pipe(
795
+ createElement('gmd:topicCategory'),
796
+ createChild('gmd:MD_TopicCategoryCode'),
797
+ setTextContent(topic)
798
+ )
799
+ )
800
+ )
753
801
  )(rootEl)
754
802
  }
755
803
 
@@ -133,7 +133,7 @@ export class ElasticsearchService {
133
133
  title: string,
134
134
  uuid: string,
135
135
  size = 6,
136
- _source = ES_SOURCE_SUMMARY
136
+ _source = [...ES_SOURCE_SUMMARY, 'allKeywords', 'createDate']
137
137
  ): EsSearchParams {
138
138
  return {
139
139
  query: {
@@ -60,6 +60,22 @@ export type SpatialRepresentationType =
60
60
  | 'table'
61
61
  | 'point'
62
62
 
63
+ export type KeywordType = 'place' | 'temporal' | 'theme' | 'other'
64
+
65
+ export type KeywordThesaurus = {
66
+ id: string
67
+ name?: string
68
+ url?: URL
69
+ }
70
+
71
+ export interface Keyword {
72
+ label: string
73
+ type: KeywordType
74
+ thesaurus?: KeywordThesaurus
75
+ }
76
+ // languages should be expressed using two-letters ISO 639-1 codes
77
+ export type LanguageCode = string
78
+
63
79
  export interface BaseRecord {
64
80
  uniqueIdentifier: Uuid
65
81
  ownerOrganization: Organization
@@ -68,9 +84,11 @@ export interface BaseRecord {
68
84
  abstract: string
69
85
  recordCreated?: Date
70
86
  recordUpdated: Date
87
+ recordPublished?: Date
88
+ languages: Array<LanguageCode>
71
89
  kind: RecordKind
72
- themes: Array<string> // TODO: handle codelists
73
- keywords: Array<string> // TODO: handle thesaurus and id
90
+ topics: Array<string> // TODO: handle codelists
91
+ keywords: Array<Keyword>
74
92
  licenses: Array<Constraint>
75
93
  legalConstraints: Array<Constraint>
76
94
  securityConstraints: Array<Constraint>
@@ -80,7 +98,6 @@ export interface BaseRecord {
80
98
  landingPage?: URL
81
99
  updateFrequency?: UpdateFrequency
82
100
 
83
- // to add: iso19139.topicCategory
84
101
  // to add: canonical url
85
102
  // to add: source catalog (??)
86
103
  // to add: is open data ?
@@ -131,7 +131,7 @@ export const ES_FIXTURE_FULL_RESPONSE = deepFreeze({
131
131
  creationDateForResource: ['2012-01-01T00:00:00.000Z'],
132
132
  creationYearForResource: '2012',
133
133
  creationMonthForResource: '2012-01',
134
- publicationDateForResource: ['2021-04-01T00:00:00.000Z'],
134
+ publicationDateForResource: '2021-04-01T00:00:00.000Z',
135
135
  publicationYearForResource: '2021',
136
136
  publicationMonthForResource: '2021-04',
137
137
  resourceDate: [
@@ -69,8 +69,24 @@ Cette section contient des *caractères internationaux* (ainsi que des "caractè
69
69
  url: new URL('http://my-org.net/two.png'),
70
70
  },
71
71
  ],
72
- keywords: ['international', 'test', '_another_keyword_'],
73
- themes: ['testData', 'exampleData'],
72
+ keywords: [
73
+ {
74
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
75
+ type: 'other',
76
+ label: 'international',
77
+ },
78
+ {
79
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
80
+ type: 'other',
81
+ label: 'test',
82
+ },
83
+ {
84
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
85
+ type: 'other',
86
+ label: '_another_keyword_',
87
+ },
88
+ ],
89
+ topics: ['testData', 'exampleData'],
74
90
  spatialRepresentation: 'grid',
75
91
  distributions: [
76
92
  {
@@ -133,6 +149,7 @@ As such, **it is not very interesting at all.**`,
133
149
  updatedTimes: 3,
134
150
  per: 'month',
135
151
  },
152
+ languages: ['en'],
136
153
  },
137
154
  {
138
155
  uniqueIdentifier: '7d002c4c-92ef-4b9f-a568-d732f740b99e',
@@ -170,13 +187,33 @@ Malgré l'attention portée à la création de ces données, il est rappelé que
170
187
  },
171
188
  ],
172
189
  keywords: [
173
- 'planification',
174
- 'PLU',
175
- "Plan local d'urbanisme",
176
- 'données ouvertes',
177
- 'Avrigny*60036',
190
+ {
191
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
192
+ type: 'other',
193
+ label: 'planification',
194
+ },
195
+ {
196
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
197
+ type: 'other',
198
+ label: 'PLU',
199
+ },
200
+ {
201
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
202
+ type: 'other',
203
+ label: "Plan local d'urbanisme",
204
+ },
205
+ {
206
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
207
+ type: 'other',
208
+ label: 'données ouvertes',
209
+ },
210
+ {
211
+ thesaurus: { id: 'geonetwork.thesaurus.local' },
212
+ type: 'other',
213
+ label: 'Avrigny*60036',
214
+ },
178
215
  ],
179
- themes: ['Usage des sols', "document d'urbanisme"],
216
+ topics: ['Usage des sols', "document d'urbanisme"],
180
217
  spatialRepresentation: 'vector',
181
218
  distributions: [
182
219
  {
@@ -204,5 +241,6 @@ Ce lot de données produit en 2019, a été numérisé à partir du PCI Vecteur
204
241
  temporalExtents: [],
205
242
  status: 'completed',
206
243
  updateFrequency: 'unknown',
244
+ languages: ['fr', 'de'],
207
245
  },
208
246
  ])
@@ -29,18 +29,18 @@ export class DataViewPermalinkComponent {
29
29
  if (config) {
30
30
  const { aggregation, xProperty, yProperty, chartType } = config
31
31
  const url = new URL(`${this.wcEmbedderBaseUrl}`, window.location.origin)
32
- url.search = `?v=${this.version}
33
- &e=gn-dataset-view-chart
34
- &a=api-url=${this.config.basePath}
35
- &a=dataset-id=${metadata.uniqueIdentifier}
36
- &a=primary-color=%230f4395
37
- &a=secondary-color=%238bc832
38
- &a=main-color=%23555
39
- &a=background-color=%23fdfbff
40
- &a=aggregation=${aggregation}
41
- &a=x-property=${xProperty}
42
- &a=y-property=${yProperty}
43
- &a=chart-type=${chartType}`
32
+ url.searchParams.set('v', `${this.version}`)
33
+ url.searchParams.append('e', `gn-dataset-view-chart`)
34
+ url.searchParams.append('a', `api-url=${this.config.basePath}`)
35
+ url.searchParams.append('a', `dataset-id=${metadata.uniqueIdentifier}`)
36
+ url.searchParams.append('a', `primary-color=#0f4395`)
37
+ url.searchParams.append('a', `secondary-color=#8bc832`)
38
+ url.searchParams.append('a', `main-color=#555`)
39
+ url.searchParams.append('a', `background-color=#fdfbff`)
40
+ url.searchParams.append('a', `aggregation=${aggregation}`)
41
+ url.searchParams.append('a', `x-property=${xProperty}`)
42
+ url.searchParams.append('a', `y-property=${yProperty}`)
43
+ url.searchParams.append('a', `chart-type=${chartType}`)
44
44
  return url.toString()
45
45
  }
46
46
  return ''
@@ -1,6 +1,6 @@
1
- <div class="flex flex-row content-center">
1
+ <div class="flex flex-row items-center">
2
2
  <span
3
- class="align-text-top mr-1.5 mt-1.5 favorite-count"
3
+ class="align-text-top mr-1.5 favorite-count"
4
4
  *ngIf="hasFavoriteCount && displayCount"
5
5
  >{{ favoriteCount }}</span
6
6
  >
@@ -8,10 +8,9 @@
8
8
  [toggled]="isFavorite$ | async"
9
9
  (newValue)="toggleFavorite($event)"
10
10
  [disabled]="loading || (isAnonymous$ | async)"
11
- class="mt-1"
12
11
  ></gn-ui-star-toggle>
13
12
  <span
14
- class="align-text-top ml-1.5 favorite-count mt-1.5"
13
+ class="align-text-top ml-1.5 favorite-count"
15
14
  *ngIf="!displayCount"
16
15
  translate="datahub.record.addToFavorites"
17
16
  ></span>
@@ -5,6 +5,6 @@
5
5
  [selected]="currentLang"
6
6
  ariaName="languages"
7
7
  [showTitle]="false"
8
- [extraBtnClass]="'flex justify-items-center text-white !pl-2 !py-1'"
8
+ extraBtnClass="bg-transparent flex justify-items-center !pl-2 !py-1"
9
9
  >
10
10
  </gn-ui-dropdown-selector>
@@ -0,0 +1,5 @@
1
+ .icon-small {
2
+ font-size: 16px;
3
+ height: 16px;
4
+ width: 16px;
5
+ }
@@ -0,0 +1,39 @@
1
+ <div class="h-full flex flex-col">
2
+ <div class="flex-none w-full flex flex-row items-center">
3
+ <p class="flex-none font-bold">{{ label }}</p>
4
+ <div class="flex-1 flex justify-end items-center">
5
+ <gn-ui-button
6
+ [extraClass]="getButtonExtraClass()"
7
+ (buttonClick)="togglePreview()"
8
+ >
9
+ <span class="material-symbols-outlined mr-1 icon-small">{{
10
+ preview ? 'visibility' : 'visibility_off'
11
+ }}</span>
12
+ {{ preview ? 'WYSIWYG' : 'Markdown' }}
13
+ </gn-ui-button>
14
+ <span
15
+ class="material-symbols-outlined m-2 icon-small"
16
+ [matTooltip]="tooltip"
17
+ matTooltipPosition="above"
18
+ >
19
+ help
20
+ </span>
21
+ </div>
22
+ </div>
23
+ <p class="flex-none mb-2 font-medium text-sm text-gray-900">
24
+ {{ helperText }}
25
+ </p>
26
+ <div class="flex-1" [hidden]="preview">
27
+ <gn-ui-text-area
28
+ [placeholder]="placeholder"
29
+ [value]="textContent"
30
+ (valueChange)="textContentChangedHandler($event)"
31
+ ></gn-ui-text-area>
32
+ </div>
33
+ <div
34
+ class="flex-1 border border-gray-800 rounded overflow-y-scroll"
35
+ [hidden]="!preview"
36
+ >
37
+ <gn-ui-markdown-parser [textContent]="textContent"></gn-ui-markdown-parser>
38
+ </div>
39
+ </div>