geonetwork-ui 2.10.0-dev.89f0dfe6f → 2.10.0-dev.8f8114b9e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/geonetwork-ui.mjs +83 -9
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +18 -2
- package/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +54 -32
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +46 -0
- package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
- package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
- package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +0 -2
- package/src/libs/feature/editor/src/lib/services/editor.service.ts +1 -1
- package/src/libs/util/app-config/src/lib/app-config.ts +14 -1
- package/src/libs/util/app-config/src/lib/model.ts +3 -0
- package/src/libs/util/app-config/src/lib/parse-utils.ts +27 -0
- package/src/libs/util/shared/src/lib/links/link-utils.ts +1 -1
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { Injectable, InjectionToken, inject } from '@angular/core'
|
|
7
7
|
import {
|
|
8
8
|
assertValidXml,
|
|
9
|
+
BaseConverter,
|
|
9
10
|
findConverterForDocument,
|
|
10
11
|
Gn4Converter,
|
|
11
12
|
Gn4SearchResults,
|
|
@@ -61,6 +62,12 @@ export const DISABLE_DRAFT = new InjectionToken<boolean>('gnDisableDraft', {
|
|
|
61
62
|
factory: () => false,
|
|
62
63
|
})
|
|
63
64
|
|
|
65
|
+
export const DEFAULT_RECORD_CONVERTER = new InjectionToken<
|
|
66
|
+
BaseConverter<string>
|
|
67
|
+
>('defaultRecordConverter', {
|
|
68
|
+
factory: () => new Iso19139Converter(),
|
|
69
|
+
})
|
|
70
|
+
|
|
64
71
|
@Injectable()
|
|
65
72
|
export class Gn4Repository implements RecordsRepositoryInterface {
|
|
66
73
|
private httpClient = inject(HttpClient)
|
|
@@ -72,6 +79,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
72
79
|
private gn4LanguagesApi = inject(LanguagesApiService)
|
|
73
80
|
private settingsService = inject(Gn4SettingsService)
|
|
74
81
|
private disableDraft = inject(DISABLE_DRAFT, { optional: true }) ?? false
|
|
82
|
+
private defaultConverter = inject(DEFAULT_RECORD_CONVERTER)
|
|
75
83
|
|
|
76
84
|
_draftsChanged = new Subject<void>()
|
|
77
85
|
draftsChanged$ = this._draftsChanged.asObservable()
|
|
@@ -377,38 +385,52 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
377
385
|
openRecordForDuplication(
|
|
378
386
|
uniqueIdentifier: string
|
|
379
387
|
): Observable<[CatalogRecord, string, true] | null> {
|
|
380
|
-
return this.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
+
})
|
|
409
431
|
)
|
|
410
|
-
})
|
|
411
432
|
)
|
|
433
|
+
)
|
|
412
434
|
}
|
|
413
435
|
|
|
414
436
|
saveRecord(
|
|
@@ -605,10 +627,10 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
605
627
|
record: CatalogRecord,
|
|
606
628
|
referenceRecordSource?: string
|
|
607
629
|
): Observable<string> {
|
|
608
|
-
// if there's a reference record, use that standard; otherwise, use
|
|
630
|
+
// if there's a reference record, use that standard; otherwise, use standard based on configuration or default
|
|
609
631
|
const converter = referenceRecordSource
|
|
610
632
|
? findConverterForDocument(referenceRecordSource)
|
|
611
|
-
:
|
|
633
|
+
: this.defaultConverter
|
|
612
634
|
return from(converter.writeRecord(record, referenceRecordSource))
|
|
613
635
|
}
|
|
614
636
|
|
|
@@ -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)
|
|
@@ -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?:\/\//)) {
|
|
@@ -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[]>
|
|
@@ -359,7 +359,6 @@ export const simpleDatasetRecordFixture = (): DatasetRecord => ({
|
|
|
359
359
|
securityConstraints: [],
|
|
360
360
|
otherConstraints: [],
|
|
361
361
|
lineage: 'This record was edited manually to test the conversion processes',
|
|
362
|
-
sourceRecords: [],
|
|
363
362
|
spatialRepresentation: 'grid',
|
|
364
363
|
overviews: [],
|
|
365
364
|
spatialExtents: [],
|
|
@@ -1000,7 +999,6 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
|
|
|
1000
999
|
},
|
|
1001
1000
|
},
|
|
1002
1001
|
],
|
|
1003
|
-
sourceRecords: [],
|
|
1004
1002
|
onlineResources: [],
|
|
1005
1003
|
licenses: [],
|
|
1006
1004
|
legalConstraints: [],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Injectable, inject } from '@angular/core'
|
|
2
|
-
import {
|
|
2
|
+
import { Observable, switchMap } from 'rxjs'
|
|
3
3
|
import { map, tap } from 'rxjs/operators'
|
|
4
4
|
import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
5
5
|
import { EditorConfig } from '../models/'
|
|
@@ -2,6 +2,7 @@ import * as TOML from '@ltd/j-toml'
|
|
|
2
2
|
import {
|
|
3
3
|
checkMetadataLanguage,
|
|
4
4
|
checkNewRecordDefaultLanguage,
|
|
5
|
+
checkNewRecordStandard,
|
|
5
6
|
parseConfigSection,
|
|
6
7
|
parseMultiConfigSection,
|
|
7
8
|
parseTranslationsConfigSection,
|
|
@@ -297,7 +298,7 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
297
298
|
parsed,
|
|
298
299
|
'editing',
|
|
299
300
|
[],
|
|
300
|
-
['new_record_default_language'],
|
|
301
|
+
['new_record_default_language', 'new_record_standard'],
|
|
301
302
|
warnings,
|
|
302
303
|
errors
|
|
303
304
|
)
|
|
@@ -310,6 +311,15 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
310
311
|
warnings
|
|
311
312
|
)
|
|
312
313
|
}
|
|
314
|
+
if (
|
|
315
|
+
parsedEditingSection !== null &&
|
|
316
|
+
parsedEditingSection.new_record_standard !== undefined
|
|
317
|
+
) {
|
|
318
|
+
parsedEditingSection = checkNewRecordStandard(
|
|
319
|
+
parsedEditingSection,
|
|
320
|
+
warnings
|
|
321
|
+
)
|
|
322
|
+
}
|
|
313
323
|
editorConfig =
|
|
314
324
|
parsedEditingSection === null
|
|
315
325
|
? null
|
|
@@ -318,6 +328,9 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
318
328
|
parsedEditingSection.new_record_default_language as
|
|
319
329
|
| string
|
|
320
330
|
| undefined,
|
|
331
|
+
NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard as
|
|
332
|
+
| EditorConfig['NEW_RECORD_STANDARD']
|
|
333
|
+
| undefined,
|
|
321
334
|
} as EditorConfig)
|
|
322
335
|
|
|
323
336
|
customTranslations = parseTranslationsConfigSection(
|
|
@@ -69,8 +69,11 @@ export interface MetadataQualityConfig {
|
|
|
69
69
|
ENABLED: boolean
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
export type NewRecordStandard = 'iso19139' | 'iso19115-3'
|
|
73
|
+
|
|
72
74
|
export interface EditorConfig {
|
|
73
75
|
NEW_RECORD_DEFAULT_LANGUAGE?: string
|
|
76
|
+
NEW_RECORD_STANDARD?: NewRecordStandard
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
export type CustomTranslations = { [translationKey: string]: string }
|
|
@@ -159,3 +159,30 @@ export function checkNewRecordDefaultLanguage(
|
|
|
159
159
|
new_record_default_language: lang2,
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
+
|
|
163
|
+
export function checkNewRecordStandard(
|
|
164
|
+
parsedConfigSection: any,
|
|
165
|
+
outWarnings: string[]
|
|
166
|
+
) {
|
|
167
|
+
const standard = parsedConfigSection.new_record_standard
|
|
168
|
+
const normalizedStandard =
|
|
169
|
+
typeof standard === 'string' ? standard.trim().toLowerCase() : null
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
normalizedStandard === 'iso19139' ||
|
|
173
|
+
normalizedStandard === 'iso19115-3'
|
|
174
|
+
) {
|
|
175
|
+
return {
|
|
176
|
+
...parsedConfigSection,
|
|
177
|
+
new_record_standard: normalizedStandard,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
outWarnings.push(
|
|
182
|
+
`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`
|
|
183
|
+
)
|
|
184
|
+
return {
|
|
185
|
+
...parsedConfigSection,
|
|
186
|
+
new_record_standard: undefined,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -266,7 +266,7 @@ export function checkFileFormat(
|
|
|
266
266
|
new RegExp(`[./]${format}`, 'i').test(link.name.toLowerCase())) ||
|
|
267
267
|
('url' in link &&
|
|
268
268
|
new RegExp(`[./]${format}`, 'i').test(link.url.toString())) ||
|
|
269
|
-
('name' in link &&
|
|
269
|
+
('name' in link && new RegExp(`\\b${format}\\b`, 'i').test(link.name))
|
|
270
270
|
)
|
|
271
271
|
}
|
|
272
272
|
|