geonetwork-ui 2.10.0-dev.cf0577fae → 2.10.0-dev.d26e9afe1

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 (101) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1035 -6108
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +147 -154
  4. package/index.d.ts.map +1 -1
  5. package/material-styles.css +483 -0
  6. package/package.json +25 -35
  7. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
  8. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +5 -4
  9. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  10. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  11. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -4
  12. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  13. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +49 -35
  14. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  15. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  16. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +48 -2
  17. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -5
  18. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  19. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  20. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  21. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  22. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +51 -0
  23. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  24. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +4 -1
  25. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
  26. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
  27. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  28. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  29. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  30. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +4 -1
  31. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +6 -7
  32. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  33. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  34. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  35. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  36. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  37. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  38. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  39. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +0 -34
  40. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +20 -16
  41. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +9 -0
  42. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  43. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  44. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +76 -19
  45. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  46. package/src/libs/feature/editor/src/lib/fields.config.ts +310 -336
  47. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +2 -3
  48. package/src/libs/feature/editor/src/lib/services/editor.service.ts +8 -12
  49. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  50. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  51. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  52. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  53. package/src/libs/ui/dataviz/src/index.ts +0 -1
  54. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +48 -56
  55. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +7 -3
  56. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  57. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  58. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  59. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  60. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +18 -8
  61. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +29 -1
  62. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  63. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  64. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  65. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  66. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  67. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  68. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  69. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  70. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  71. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  72. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  73. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  74. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
  75. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  76. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  77. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  78. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  79. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  80. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  81. package/src/libs/util/app-config/src/lib/app-config.ts +21 -9
  82. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  83. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  84. package/src/libs/util/i18n/src/index.ts +0 -1
  85. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  86. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  87. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  88. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  89. package/src/libs/util/shared/src/lib/links/link-utils.ts +1 -1
  90. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  91. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  92. package/style.css +0 -1
  93. package/tailwind.base.css +8 -0
  94. package/translations/de.json +50 -47
  95. package/translations/en.json +55 -52
  96. package/translations/es.json +24 -21
  97. package/translations/fr.json +50 -47
  98. package/translations/it.json +50 -47
  99. package/translations/nl.json +23 -20
  100. package/translations/pt.json +24 -21
  101. package/translations/sk.json +24 -21
@@ -1,4 +1,4 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import { Configuration } from '../../../../../../libs/data-access/gn4/src'
3
3
  import { TranslateService } from '@ngx-translate/core'
4
4
  import { toLang3 } from '../../../../../../libs/util/i18n/src'
@@ -13,7 +13,7 @@ export class MetadataUrlService {
13
13
  getUrl(uuid: string, apiPath: string = this.apiConfiguration.basePath) {
14
14
  const prefix = `${apiPath}/../`
15
15
  return `${prefix}${toLang3(
16
- this.translate.currentLang
16
+ this.translate.getCurrentLang()
17
17
  )}/catalog.search#/metadata/${uuid}`
18
18
  }
19
19
  }
@@ -62,6 +62,7 @@ type ThesaurusName = string
62
62
  export interface Thesaurus {
63
63
  id?: string
64
64
  title?: string
65
+ multilingualTitle?: MultilingualField
65
66
  theme?: string
66
67
  link?: string
67
68
  keywords: MultilingualField[]
@@ -1,4 +1,4 @@
1
- import { Injectable, InjectionToken, inject } from '@angular/core'
1
+ import { inject, Injectable, InjectionToken } from '@angular/core'
2
2
  import { toLang2, toLang3 } from '../../../../../../../libs/util/i18n/src'
3
3
  import { TranslateService } from '@ngx-translate/core'
4
4
  import { Location } from '@angular/common'
@@ -48,8 +48,8 @@ export class AuthService {
48
48
  '${current_path}',
49
49
  this.location.prepareExternalUrl(this.location.path())
50
50
  )
51
- .replace('${lang2}', toLang2(this.translateService.currentLang))
52
- .replace('${lang3}', toLang3(this.translateService.currentLang))
51
+ .replace('${lang2}', toLang2(this.translateService.getCurrentLang()))
52
+ .replace('${lang3}', toLang3(this.translateService.getCurrentLang()))
53
53
  }
54
54
 
55
55
  get logoutUrl() {
@@ -59,7 +59,7 @@ export class AuthService {
59
59
  get settingsUrl() {
60
60
  return this.baseSettingsUrl.replace(
61
61
  '${lang3}',
62
- toLang3(this.translateService.currentLang)
62
+ toLang3(this.translateService.getCurrentLang())
63
63
  )
64
64
  }
65
65
  }
@@ -1,4 +1,4 @@
1
- import { Injectable, Injector, inject } from '@angular/core'
1
+ import { inject, Injectable, Injector } from '@angular/core'
2
2
  import type { Geometry } from 'geojson'
3
3
  import {
4
4
  ES_QUERY_FIELDS_PRIORITY,
@@ -234,7 +234,7 @@ export class ElasticsearchService {
234
234
  if (this.metadataLang) {
235
235
  const lang3 = toLang3(
236
236
  this.isCurrentSearchLang()
237
- ? this.translateService.currentLang
237
+ ? this.translateService.getCurrentLang()
238
238
  : this.metadataLang
239
239
  )
240
240
  return `lang${lang3}`
@@ -3,7 +3,7 @@ import {
3
3
  HttpErrorResponse,
4
4
  HttpHeaders,
5
5
  } from '@angular/common/http'
6
- import { Injectable, InjectionToken, inject } from '@angular/core'
6
+ import { inject, Injectable, InjectionToken } from '@angular/core'
7
7
  import {
8
8
  assertValidXml,
9
9
  BaseConverter,
@@ -336,10 +336,10 @@ export class Gn4Repository implements RecordsRepositoryInterface {
336
336
 
337
337
  private canEdit(record: CatalogRecord, allowEditHarvested: boolean): boolean {
338
338
  return (
339
- this.platformService.supportsAuthentication() &&
340
- record.kind === 'dataset' &&
341
- record.extras['edit'] &&
342
- (!record.extras['isHarvested'] || allowEditHarvested)
339
+ (this.platformService.supportsAuthentication() &&
340
+ record.extras?.['edit'] &&
341
+ (!record.extras?.['isHarvested'] || allowEditHarvested)) ??
342
+ false
343
343
  )
344
344
  }
345
345
 
@@ -385,38 +385,52 @@ export class Gn4Repository implements RecordsRepositoryInterface {
385
385
  openRecordForDuplication(
386
386
  uniqueIdentifier: string
387
387
  ): Observable<[CatalogRecord, string, true] | null> {
388
- return this.gn4RecordsApi
389
- .create(
390
- uniqueIdentifier,
391
- '2',
392
- 'METADATA',
393
- '',
394
- false,
395
- undefined,
396
- true,
397
- false,
398
- undefined,
399
- 'body',
400
- false,
401
- {
402
- httpHeaderAccept: 'application/json',
403
- httpContentTypeSelected: 'application/json;charset=UTF-8',
404
- }
405
- )
406
- .pipe(
407
- switchMap((uniqueIdentifier) => {
408
- return this.getRecordAsXml(uniqueIdentifier)
409
- }),
410
- switchMap((xml) => {
411
- return from(
412
- findConverterForDocument(xml)
413
- .readRecord(xml)
414
- .then((record) => {
415
- return [record, xml, true] as [CatalogRecord, string, true]
416
- })
388
+ return this.platformService.getUserPermissionsByGroup().pipe(
389
+ map((permissions) => {
390
+ const groupId =
391
+ permissions.find((p) => p.canApprove)?.groupId?.toString() ??
392
+ permissions.find((p) => p.canEdit)?.groupId?.toString()
393
+ if (!groupId)
394
+ throw new Error(
395
+ 'Current user has no writable group to duplicate into'
396
+ )
397
+ return groupId
398
+ }),
399
+ switchMap((groupId) =>
400
+ this.gn4RecordsApi
401
+ .create(
402
+ uniqueIdentifier,
403
+ groupId,
404
+ 'METADATA',
405
+ '',
406
+ false,
407
+ undefined,
408
+ true,
409
+ false,
410
+ undefined,
411
+ 'body',
412
+ false,
413
+ {
414
+ httpHeaderAccept: 'application/json',
415
+ httpContentTypeSelected: 'application/json;charset=UTF-8',
416
+ }
417
+ )
418
+ .pipe(
419
+ switchMap((uniqueIdentifier) => {
420
+ return this.getRecordAsXml(uniqueIdentifier)
421
+ }),
422
+ switchMap((xml) => {
423
+ return from(
424
+ findConverterForDocument(xml)
425
+ .readRecord(xml)
426
+ .then((record) => {
427
+ return [record, xml, true] as [CatalogRecord, string, true]
428
+ })
429
+ )
430
+ })
417
431
  )
418
- })
419
432
  )
433
+ )
420
434
  }
421
435
 
422
436
  saveRecord(
@@ -52,7 +52,7 @@ export class OrganizationsFromGroupsService
52
52
  organisationsCount$ = this.organisations$.pipe(map((orgs) => orgs.length))
53
53
 
54
54
  private get lang3() {
55
- return toLang3(this.translateService.currentLang)
55
+ return toLang3(this.translateService.getCurrentLang())
56
56
  }
57
57
 
58
58
  constructor(
@@ -128,7 +128,7 @@ export class OrganizationsFromMetadataService
128
128
  ) {}
129
129
 
130
130
  private get langIndex() {
131
- return `lang${toLang3(this.translateService.currentLang)}`
131
+ return `lang${toLang3(this.translateService.getCurrentLang())}`
132
132
  }
133
133
 
134
134
  equalsNormalizedStrings(
@@ -1,5 +1,5 @@
1
1
  import { HttpClient, HttpEventType } from '@angular/common/http'
2
- import { Injectable, InjectionToken, inject } from '@angular/core'
2
+ import { inject, Injectable, InjectionToken } from '@angular/core'
3
3
  import {
4
4
  KeywordApiResponse,
5
5
  ThesaurusApiResponse,
@@ -12,11 +12,13 @@ import {
12
12
  } from '../../../../../../../libs/common/domain/src/lib/model/record'
13
13
  import { KeywordType } from '../../../../../../../libs/common/domain/src/lib/model/thesaurus'
14
14
  import { UserModel } from '../../../../../../../libs/common/domain/src/lib/model/user/user.model'
15
+ import { GroupModel } from '../../../../../../../libs/common/domain/src/lib/model/user/group.model'
15
16
  import {
16
17
  PlatformServiceInterface,
17
18
  UploadEvent,
18
19
  } from '../../../../../../../libs/common/domain/src/lib/platform.service.interface'
19
20
  import {
21
+ GroupsApiService,
20
22
  MeApiService,
21
23
  RecordsApiService,
22
24
  RegistriesApiService,
@@ -57,6 +59,7 @@ export const DISABLE_AUTH = new InjectionToken<boolean>('gnDisableAuth', {
57
59
  export class Gn4PlatformService implements PlatformServiceInterface {
58
60
  private meApi = inject(MeApiService)
59
61
  private usersApi = inject(UsersApiService)
62
+ private groupsApi = inject(GroupsApiService)
60
63
  private mapper = inject(Gn4PlatformMapper)
61
64
  private toolsApiService = inject(ToolsApiService)
62
65
  private registriesApiService = inject(RegistriesApiService)
@@ -97,7 +100,7 @@ export class Gn4PlatformService implements PlatformServiceInterface {
97
100
  private keywordsByThesauri: Record<string, Observable<Keyword[]>> = {}
98
101
 
99
102
  private get lang3() {
100
- return toLang3(this.translateService.currentLang)
103
+ return toLang3(this.translateService.getCurrentLang())
101
104
  }
102
105
 
103
106
  constructor() {
@@ -155,6 +158,49 @@ export class Gn4PlatformService implements PlatformServiceInterface {
155
158
  return this.users$
156
159
  }
157
160
 
161
+ getUserPermissionsByGroup(): Observable<GroupModel[]> {
162
+ if (this.disableAuth) return of([])
163
+ return combineLatest([this.meApi.getMe(), this.groupsApi.getGroups()]).pipe(
164
+ map(([meResponse, groups]) => {
165
+ if (!meResponse) return []
166
+ if (meResponse.admin) {
167
+ return groups.map((group) => ({
168
+ groupId: group.id,
169
+ groupName: group.name,
170
+ isMember: true,
171
+ canEdit: true,
172
+ canApprove: true,
173
+ canAdministrate: true,
174
+ }))
175
+ }
176
+ const reviewerIds = meResponse.groupsWithReviewer ?? []
177
+ const editorIds = meResponse.groupsWithEditor ?? []
178
+ const memberIds = meResponse.groupsWithRegisteredUser ?? []
179
+ const adminIds = meResponse.groupsWithUserAdmin ?? []
180
+ const groupsById = new Map(groups.map((g) => [g.id, g]))
181
+ return [
182
+ ...new Set([
183
+ ...reviewerIds,
184
+ ...editorIds,
185
+ ...groups.map((g) => g.id),
186
+ ]),
187
+ ]
188
+ .filter((id) => groupsById.has(id))
189
+ .map((id) => {
190
+ const group = groupsById.get(id)
191
+ return {
192
+ groupId: group.id,
193
+ groupName: group.name,
194
+ isMember: memberIds.includes(id),
195
+ canEdit: editorIds.includes(id),
196
+ canApprove: reviewerIds.includes(id),
197
+ canAdministrate: adminIds.includes(id),
198
+ }
199
+ })
200
+ })
201
+ )
202
+ }
203
+
158
204
  translateKey(key: string): Observable<string> {
159
205
  // if the key is a URI, use the registries API to look for the translation
160
206
  if (key.match(/^https?:\/\//)) {
@@ -104,15 +104,12 @@ export interface Keyword {
104
104
  type: KeywordType
105
105
  thesaurus?: ThesaurusModel
106
106
  bbox?: [number, number, number, number]
107
+ // thesaurus hierarchy path incl. the keyword as last element; unset for root-level keywords
108
+ hierarchyPath?: string[]
107
109
 
108
110
  translations?: KeywordTranslations
109
111
  }
110
112
 
111
- export interface INSPIRE_topic {
112
- value: string
113
- label: string
114
- }
115
-
116
113
  export interface ResourceIdentifier {
117
114
  code: string
118
115
  codeSpace?: string
@@ -1,7 +1,8 @@
1
1
  export type KeywordType = 'place' | 'temporal' | 'theme' | 'other'
2
2
 
3
3
  export interface ThesaurusModel {
4
- id: string
4
+ // absent when the source provides no identifier for the thesaurus
5
+ id?: string
5
6
  name?: string
6
7
  url?: URL
7
8
  thesaurusKey?: string
@@ -0,0 +1,8 @@
1
+ export interface GroupModel {
2
+ groupId: number
3
+ groupName: string
4
+ isMember: boolean
5
+ canEdit: boolean
6
+ canApprove: boolean
7
+ canAdministrate: boolean
8
+ }
@@ -1 +1,2 @@
1
1
  export * from './user.model'
2
+ export * from './group.model'
@@ -1,5 +1,6 @@
1
1
  import type { Observable } from 'rxjs'
2
2
  import type { UserModel } from './model/user/user.model'
3
+ import type { GroupModel } from './model/user/group.model'
3
4
  import type { Organization } from './model/record/organization.model'
4
5
  import { CatalogRecord, Keyword, UserFeedback } from './model/record'
5
6
  import { KeywordType } from './model/thesaurus'
@@ -28,6 +29,7 @@ export abstract class PlatformServiceInterface {
28
29
  abstract getMe(): Observable<UserModel>
29
30
  abstract isAnonymous(): Observable<boolean>
30
31
  abstract getUsers(): Observable<UserModel[]>
32
+ abstract getUserPermissionsByGroup(): Observable<GroupModel[]>
31
33
  abstract getUsersByOrganization(
32
34
  organisation: Organization
33
35
  ): Observable<UserModel[]>
@@ -3,6 +3,7 @@ import {
3
3
  DatasetRecord,
4
4
  DatasetSpatialExtent,
5
5
  Keyword,
6
+ ReuseRecord,
6
7
  ServiceRecord,
7
8
  } from '../../../../../libs/common/domain/src/lib/model/record'
8
9
 
@@ -1027,3 +1028,53 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
1027
1028
  topics: [],
1028
1029
  recordUpdated: null,
1029
1030
  })
1031
+
1032
+ export const simpleReuseRecordFixture = (): ReuseRecord => ({
1033
+ abstract: `Sample reuse record`,
1034
+ kind: 'reuse',
1035
+ reuseType: 'application',
1036
+ recordUpdated: new Date('2023-03-17T07:38:08.875Z'),
1037
+ recordPublished: null,
1038
+ ownerOrganization: null,
1039
+ licenses: [],
1040
+ legalConstraints: [],
1041
+ securityConstraints: [],
1042
+ otherConstraints: [],
1043
+ contacts: [],
1044
+ contactsForResource: [],
1045
+ keywords: [],
1046
+ topics: [],
1047
+ spatialExtents: [],
1048
+ temporalExtents: [],
1049
+ lineage: 'bla',
1050
+ sourceRecords: [],
1051
+ overviews: [],
1052
+ defaultLanguage: null,
1053
+ otherLanguages: [],
1054
+ title: 'Sites de gestion des déchets miniers - Service de visualisation WMS',
1055
+ onlineResources: [
1056
+ {
1057
+ name: 'Rapport de disponibilité du service WMS',
1058
+ description:
1059
+ 'Ce service de visualisation WMS permet de consulter la série de couches de données "Sites de gestion des déchets miniers - Série".',
1060
+ mimeType: 'text/html',
1061
+ type: 'link',
1062
+ url: new URL(
1063
+ 'https://geoservices.wallonie.be/rapportDisponibilite/wms/sites_de_gestion_des_dechets_miniers_srie.html'
1064
+ ),
1065
+ accessRestricted: false,
1066
+ },
1067
+ ],
1068
+ uniqueIdentifier: '00b22798-ec8e-4500-89e8-90eeeda45919',
1069
+ landingPage: new URL(
1070
+ 'http://localhost:4200/geonetwork/srv/fre/catalog.search#/metadata/00b22798-ec8e-4500-89e8-90eeeda45919'
1071
+ ),
1072
+ extras: {
1073
+ isPublishedToAll: true,
1074
+ id: '723',
1075
+ isHarvested: true,
1076
+ ownerInfo: 'admin|admin|admin|Administrator',
1077
+ edit: true,
1078
+ },
1079
+ recordCreated: new Date('2023-03-17T07:38:08.875Z'),
1080
+ })
@@ -1,4 +1,4 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import { SourcesApiService } from '../../../../../../libs/data-access/gn4/src'
3
3
  import { Observable } from 'rxjs'
4
4
  import { filter, map, shareReplay } from 'rxjs/operators'
@@ -21,7 +21,10 @@ export class SourcesService {
21
21
  return this.sources$.pipe(
22
22
  map((sources) => sources.filter((source) => source.uuid === uuid)[0]),
23
23
  filter((source) => !!source),
24
- map((source) => source.label[toLang3(this.translateService.currentLang)])
24
+ map(
25
+ (source) =>
26
+ source.label[toLang3(this.translateService.getCurrentLang())]
27
+ )
25
28
  )
26
29
  }
27
30
  }
@@ -29,7 +29,10 @@ export const markRecordAsChanged = createAction(
29
29
  '[Editor] Mark record as changed'
30
30
  )
31
31
 
32
- export const saveRecord = createAction('[Editor] Save record')
32
+ export const saveRecord = createAction(
33
+ '[Editor] Save record',
34
+ props<{ publish: boolean }>()
35
+ )
33
36
  export const saveRecordSuccess = createAction('[Editor] Save record success')
34
37
  export const saveRecordFailure = createAction(
35
38
  '[Editor] Save record failure',
@@ -28,30 +28,37 @@ export class EditorEffects {
28
28
  this.store.select(selectRecordSource),
29
29
  this.store.select(selectEditorConfig)
30
30
  ),
31
- switchMap(([, record, recordSource, fieldsConfig]) =>
32
- this.editorService.saveRecord(record, recordSource, fieldsConfig).pipe(
33
- switchMap(([savedRecord, savedRecordSource]) => {
34
- const actions: Action[] = [EditorActions.saveRecordSuccess()]
31
+ switchMap(([{ publish }, record, recordSource, fieldsConfig]) =>
32
+ this.editorService
33
+ .saveRecord(record, recordSource, fieldsConfig, publish)
34
+ .pipe(
35
+ switchMap(([savedRecord, savedRecordSource]) => {
36
+ const actions: Action[] = [EditorActions.saveRecordSuccess()]
37
+
38
+ if (publish) {
39
+ // saving without publishing doesn't unpublish
40
+ actions.push(EditorActions.isPublished({ isPublished: true }))
41
+ }
35
42
 
36
- if (!record?.uniqueIdentifier) {
37
- actions.push(
38
- EditorActions.openRecord({
39
- record: savedRecord,
40
- recordSource: savedRecordSource,
43
+ if (!record?.uniqueIdentifier) {
44
+ actions.push(
45
+ EditorActions.openRecord({
46
+ record: savedRecord,
47
+ recordSource: savedRecordSource,
48
+ })
49
+ )
50
+ }
51
+
52
+ return of(...actions)
53
+ }),
54
+ catchError((error) =>
55
+ of(
56
+ EditorActions.saveRecordFailure({
57
+ error,
41
58
  })
42
59
  )
43
- }
44
-
45
- return of(...actions)
46
- }),
47
- catchError((error) =>
48
- of(
49
- EditorActions.saveRecordFailure({
50
- error,
51
- })
52
60
  )
53
61
  )
54
- )
55
62
  )
56
63
  )
57
64
  )
@@ -54,7 +54,11 @@ export class EditorFacade {
54
54
  }
55
55
 
56
56
  saveRecord() {
57
- this.store.dispatch(EditorActions.saveRecord())
57
+ this.store.dispatch(EditorActions.saveRecord({ publish: false }))
58
+ }
59
+
60
+ saveAndPublishRecord() {
61
+ this.store.dispatch(EditorActions.saveRecord({ publish: true }))
58
62
  }
59
63
 
60
64
  undoRecordDraft() {
@@ -8,7 +8,6 @@ import {
8
8
 
9
9
  import { FormFieldRichComponent } from '../record-form/form-field/form-field-rich/form-field-rich.component'
10
10
  import { ButtonComponent, UrlInputComponent } from '../../../../../../../libs/ui/inputs/src'
11
- import { MatIconModule } from '@angular/material/icon'
12
11
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
13
12
  import { Constraint } from '../../../../../../../libs/common/domain/src/lib/model/record'
14
13
  import {
@@ -26,7 +25,6 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
26
25
  FormFieldRichComponent,
27
26
  UrlInputComponent,
28
27
  ButtonComponent,
29
- MatIconModule,
30
28
  TranslateDirective,
31
29
  TranslatePipe,
32
30
  NgIconComponent,
@@ -3,13 +3,13 @@
3
3
  editor.record.form.field.onlineResource.edit.protocol
4
4
  </h3>
5
5
  @if (protocolHint) {
6
- <span
7
- class="material-symbols-outlined m-2 gn-ui-icon-small"
6
+ <ng-icon
7
+ name="matHelpOutline"
8
+ size="16"
9
+ class="m-2"
8
10
  [matTooltip]="protocolHint"
9
11
  matTooltipPosition="above"
10
- >
11
- help
12
- </span>
12
+ ></ng-icon>
13
13
  }
14
14
  </div>
15
15
  <div class="flex flex-row items-center gap-4 h-[48px]">
@@ -30,6 +30,7 @@ import {
30
30
  provideNgIconsConfig,
31
31
  } from '@ng-icons/core'
32
32
  import { iconoirRefresh } from '@ng-icons/iconoir'
33
+ import { matHelpOutline } from '@ng-icons/material-icons/outline'
33
34
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
34
35
  import {
35
36
  BehaviorSubject,
@@ -65,7 +66,7 @@ marker(
65
66
  UrlInputComponent,
66
67
  ],
67
68
  providers: [
68
- provideIcons({ iconoirRefresh }),
69
+ provideIcons({ iconoirRefresh, matHelpOutline }),
69
70
  provideNgIconsConfig({
70
71
  size: '1.5em',
71
72
  }),
@@ -10,7 +10,7 @@ export class FieldFocusDirective {
10
10
 
11
11
  private el = inject(ElementRef)
12
12
 
13
- public focusField() {
13
+ public focusField(focusInnerTarget = true) {
14
14
  setTimeout(() => {
15
15
  const host = this.el.nativeElement as HTMLElement
16
16
  const glowClass = this.gnUiFieldFocusGlowClass
@@ -25,6 +25,9 @@ export class FieldFocusDirective {
25
25
  )
26
26
 
27
27
  host.scrollIntoView({ behavior: 'smooth', block: 'start' })
28
+ if (!focusInnerTarget) {
29
+ return
30
+ }
28
31
  const target =
29
32
  host.querySelector<HTMLElement>(
30
33
  'input, textarea, select, [contenteditable="true"]'
@@ -131,13 +131,12 @@ export class FormFieldConstraintsShortcutsComponent implements OnDestroy {
131
131
  map((c) => c.length > 0),
132
132
  distinctUntilChanged()
133
133
  )
134
- combineLatest([
135
- isConstraintNotEmpty$,
136
- this.anyToggleActivated$,
137
- ]).subscribe(([isNotEmpty, anyToggleActivated]) => {
138
- const visible = isNotEmpty && !anyToggleActivated
139
- this.editorFacade.setFieldVisibility({ model }, visible)
140
- })
134
+ combineLatest([isConstraintNotEmpty$, this.anyToggleActivated$])
135
+ .pipe(takeUntil(this.onDestroy$))
136
+ .subscribe(([isNotEmpty, anyToggleActivated]) => {
137
+ const visible = isNotEmpty && !anyToggleActivated
138
+ this.editorFacade.setFieldVisibility({ model }, visible)
139
+ })
141
140
  }
142
141
  hideEmptyConstraints(this.securityConstraints$, 'securityConstraints')
143
142
  hideEmptyConstraints(this.otherConstraints$, 'otherConstraints')
@@ -3,11 +3,11 @@ import {
3
3
  ChangeDetectorRef,
4
4
  Component,
5
5
  EventEmitter,
6
+ inject,
6
7
  Input,
7
8
  Output,
8
9
  TemplateRef,
9
10
  ViewChild,
10
- inject,
11
11
  } from '@angular/core'
12
12
  import { MatDialog } from '@angular/material/dialog'
13
13
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
@@ -3,11 +3,14 @@ import {
3
3
  ChangeDetectorRef,
4
4
  Component,
5
5
  EventEmitter,
6
+ inject,
6
7
  Input,
7
8
  Output,
8
- inject,
9
9
  } from '@angular/core'
10
- import { GraphicOverview } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
10
+ import {
11
+ GraphicOverview,
12
+ RecordKind,
13
+ } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
11
14
  import { ImageInputComponent } from '../../../../../../../../../libs/ui/elements/src'
12
15
  import { PlatformServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/platform.service.interface'
13
16
  import { NotificationsService } from '../../../../../../../../../libs/feature/notifications/src'
@@ -30,6 +33,7 @@ export class FormFieldOverviewsComponent {
30
33
  private cd = inject(ChangeDetectorRef)
31
34
 
32
35
  @Input() metadataUuid: string
36
+ @Input() recordKind: RecordKind
33
37
  @Input() value: Array<GraphicOverview>
34
38
  @Output() valueChange: EventEmitter<Array<GraphicOverview>> =
35
39
  new EventEmitter()
@@ -111,13 +115,16 @@ export class FormFieldOverviewsComponent {
111
115
  {
112
116
  type: 'error',
113
117
  title: this.translateService.instant(
114
- 'editor.record.resourceError.title'
118
+ 'editor.record.resourceError.title',
119
+ { recordKind: this.recordKind }
115
120
  ),
116
121
  text: `${this.translateService.instant(
117
- 'editor.record.resourceError.body'
122
+ 'editor.record.resourceError.body',
123
+ { recordKind: this.recordKind }
118
124
  )} ${error.message}`,
119
125
  closeMessage: this.translateService.instant(
120
- 'editor.record.resourceError.closeMessage'
126
+ 'editor.record.resourceError.closeMessage',
127
+ { recordKind: this.recordKind }
121
128
  ),
122
129
  },
123
130
  undefined,