+
+
+ editor.record.form.draft.updateAlert
+
+
{
})
describe('subscriptions', () => {
- it('should add 3 subscriptions to component.subscription', () => {
+ it('should add 5 subscriptions to component.subscription', () => {
const addSpy = jest.spyOn(component.subscription, 'add')
component.ngOnInit()
- expect(addSpy).toHaveBeenCalledTimes(3)
+ expect(addSpy).toHaveBeenCalledTimes(5)
})
- it('should add 4 subscriptions to component.subscription when on /create route', () => {
+ it('should add 6 subscriptions to component.subscription when on /create route', () => {
const activatedRoute = TestBed.inject(ActivatedRoute)
activatedRoute.snapshot.routeConfig.path = '/create'
fixture.detectChanges()
const addSpy = jest.spyOn(component.subscription, 'add')
component.ngOnInit()
- expect(addSpy).toHaveBeenCalledTimes(4)
+ expect(addSpy).toHaveBeenCalledTimes(6)
})
it('unsubscribes', () => {
const unsubscribeSpy = jest.spyOn(component.subscription, 'unsubscribe')
diff --git a/apps/metadata-editor/src/app/edit/edit-page.component.ts b/apps/metadata-editor/src/app/edit/edit-page.component.ts
index bead8d580e..b2f46d20c5 100644
--- a/apps/metadata-editor/src/app/edit/edit-page.component.ts
+++ b/apps/metadata-editor/src/app/edit/edit-page.component.ts
@@ -21,7 +21,7 @@ import {
import { ButtonComponent } from '@geonetwork-ui/ui/inputs'
import { TranslateModule, TranslateService } from '@ngx-translate/core'
import { combineLatest, filter, firstValueFrom, Subscription, take } from 'rxjs'
-import { map } from 'rxjs/operators'
+import { map, skip } from 'rxjs/operators'
import { SidebarComponent } from '../dashboard/sidebar/sidebar.component'
import { PageSelectorComponent } from './components/page-selector/page-selector.component'
import { TopToolbarComponent } from './components/top-toolbar/top-toolbar.component'
@@ -57,6 +57,7 @@ export class EditPageComponent implements OnInit, OnDestroy {
isLastPage$ = combineLatest([this.currentPage$, this.pagesLength$]).pipe(
map(([currentPage, pagesCount]) => currentPage >= pagesCount - 1)
)
+ hasRecordChanged$ = this.facade.hasRecordChanged$.pipe(skip(1))
@ViewChild('scrollContainer') scrollContainer: ElementRef
@@ -136,6 +137,12 @@ export class EditPageComponent implements OnInit, OnDestroy {
})
)
+ this.subscription.add(
+ this.facade.record$.subscribe((record) => {
+ this.facade.checkHasRecordChanged(record)
+ })
+ )
+
// if we're on the /create route, go to /edit/{uuid} on first change
if (this.route.snapshot.routeConfig?.path.includes('create')) {
this.subscription.add(
@@ -161,6 +168,12 @@ export class EditPageComponent implements OnInit, OnDestroy {
this.router.navigate(['edit', savedRecord.uniqueIdentifier])
})
)
+
+ this.subscription.add(
+ this.facade.record$.subscribe((record) => {
+ this.facade.checkHasRecordChanged(record)
+ })
+ )
}
ngOnDestroy() {
@@ -192,4 +205,14 @@ export class EditPageComponent implements OnInit, OnDestroy {
top: 0,
})
}
+
+ formatDate(date: Date): string {
+ return date.toLocaleDateString(this.translateService.currentLang, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ })
+ }
}
diff --git a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html
index fdfb8f2069..fc400041ce 100644
--- a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html
+++ b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html
@@ -9,6 +9,7 @@
class="shadow-md shadow-gray-300 border-[1px] border-gray-200 overflow-hidden rounded bg-white grow mx-[32px] my-[16px] text-sm"
>
@@ -97,6 +98,17 @@ class PlatformServiceInterfaceMock {
getApiVersion = jest.fn(() => of('4.2.5'))
}
+const SAMPLE_RECORD = {
+ ...datasetRecordsFixture()[0],
+ extras: {
+ ownerInfo: 'Owner|SomeDetails',
+ },
+}
+
+const translateServiceMock = {
+ currentLang: 'fr',
+}
+
describe('Gn4Repository', () => {
let repository: Gn4Repository
let gn4Helper: ElasticsearchService
@@ -130,6 +142,10 @@ describe('Gn4Repository', () => {
provide: PlatformServiceInterface,
useClass: PlatformServiceInterfaceMock,
},
+ {
+ provide: TranslateService,
+ useValue: translateServiceMock,
+ },
],
})
repository = TestBed.inject(Gn4Repository)
@@ -749,4 +765,80 @@ describe('Gn4Repository', () => {
expect(repository.isRecordNotYetSaved('1234-5678')).toBe(false)
})
})
+ describe('hasRecordChangedSinceDraft', () => {
+ it('should return an empty array if the record is unsaved', () => {
+ // Mock dependencies
+ repository.isRecordNotYetSaved = jest.fn().mockReturnValue(true)
+ repository.recordHasDraft = jest.fn().mockReturnValue(true)
+
+ repository
+ .hasRecordChangedSinceDraft(SAMPLE_RECORD)
+ .subscribe((result) => {
+ expect(result).toEqual([])
+ })
+ })
+
+ it('should return an empty array if there is no draft', () => {
+ // Mock dependencies
+ repository.isRecordNotYetSaved = jest.fn().mockReturnValue(false)
+ repository.recordHasDraft = jest.fn().mockReturnValue(false)
+
+ repository
+ .hasRecordChangedSinceDraft(SAMPLE_RECORD)
+ .subscribe((result) => {
+ expect(result).toEqual([])
+ })
+ })
+
+ it('should return updated date and owner info if the recent record is newer than the draft', () => {
+ const mockDrafts = [
+ {
+ uniqueIdentifier: 'my-dataset-001',
+ recordUpdated: new Date('2023-01-01'),
+ },
+ ]
+ const mockRecentRecord = {
+ uniqueIdentifier: 'my-dataset-001',
+ recordUpdated: new Date('2024-01-01'),
+ extras: { ownerInfo: 'Owner|SomeDetails' },
+ }
+
+ // Mock dependencies
+ repository.isRecordNotYetSaved = jest.fn().mockReturnValue(false)
+ repository.recordHasDraft = jest.fn().mockReturnValue(true)
+ repository.getAllDrafts = jest.fn().mockReturnValue(of(mockDrafts))
+ repository.getRecord = jest.fn().mockReturnValue(of(mockRecentRecord))
+
+ repository
+ .hasRecordChangedSinceDraft(SAMPLE_RECORD)
+ .subscribe((result) => {
+ expect(result).toEqual([expect.any(String), 'Owner'])
+ })
+ })
+
+ it('should return an empty array if the draft is more recent than the recent record', () => {
+ const mockDrafts = [
+ {
+ uniqueIdentifier: 'my-dataset-001',
+ recordUpdated: new Date('2024-01-01'),
+ },
+ ]
+ const mockRecentRecord = {
+ uniqueIdentifier: 'my-dataset-001',
+ recordUpdated: new Date('2023-01-01'),
+ }
+
+ // Mock dependencies
+ repository.isRecordNotYetSaved = jest.fn().mockReturnValue(false)
+ repository.recordHasDraft = jest.fn().mockReturnValue(true)
+ repository.getAllDrafts = jest.fn().mockReturnValue(of(mockDrafts))
+ repository.getRecord = jest.fn().mockReturnValue(of(mockRecentRecord))
+
+ repository
+ .hasRecordChangedSinceDraft(SAMPLE_RECORD)
+ .subscribe((result) => {
+ expect(result).toEqual([])
+ })
+ })
+ })
})
diff --git a/libs/api/repository/src/lib/gn4/gn4-repository.ts b/libs/api/repository/src/lib/gn4/gn4-repository.ts
index 64068b7f02..c26d444852 100644
--- a/libs/api/repository/src/lib/gn4/gn4-repository.ts
+++ b/libs/api/repository/src/lib/gn4/gn4-repository.ts
@@ -31,6 +31,7 @@ import {
import {
combineLatest,
exhaustMap,
+ forkJoin,
from,
Observable,
of,
@@ -365,6 +366,44 @@ export class Gn4Repository implements RecordsRepositoryInterface {
return of(draftCount)
}
+ hasRecordChangedSinceDraft(localRecord: CatalogRecord) {
+ return of({
+ isUnsaved: this.isRecordNotYetSaved(localRecord.uniqueIdentifier),
+ hasDraft: this.recordHasDraft(localRecord.uniqueIdentifier),
+ }).pipe(
+ switchMap(({ isUnsaved, hasDraft }) => {
+ if (isUnsaved || !hasDraft) {
+ return of({ user: undefined, date: undefined })
+ }
+ return forkJoin([
+ this.getAllDrafts().pipe(
+ map((drafts) => {
+ const matchingRecord = drafts.find(
+ (draft) =>
+ draft.uniqueIdentifier === localRecord.uniqueIdentifier
+ )
+ return matchingRecord?.recordUpdated || null
+ })
+ ),
+ this.getRecord(localRecord.uniqueIdentifier),
+ ]).pipe(
+ map(([draftRecordUpdated, recentRecord]) => {
+ if (recentRecord?.recordUpdated > draftRecordUpdated) {
+ const user = recentRecord.extras?.['ownerInfo']
+ ?.toString()
+ ?.split('|')
+ return {
+ user: `${user[2]} ${user[1]}`,
+ date: recentRecord.recordUpdated,
+ }
+ }
+ return { user: undefined, date: undefined }
+ })
+ )
+ })
+ )
+ }
+
private getRecordAsXml(uniqueIdentifier: string): Observable {
return this.gn4RecordsApi
.getRecordAs(
diff --git a/libs/common/domain/src/lib/repository/records-repository.interface.ts b/libs/common/domain/src/lib/repository/records-repository.interface.ts
index b2640d069c..f04d0d5ed7 100644
--- a/libs/common/domain/src/lib/repository/records-repository.interface.ts
+++ b/libs/common/domain/src/lib/repository/records-repository.interface.ts
@@ -88,4 +88,7 @@ export abstract class RecordsRepositoryInterface {
abstract getAllDrafts(): Observable
abstract getDraftsCount(): Observable
abstract draftsChanged$: Observable
+ abstract hasRecordChangedSinceDraft(
+ localRecord: CatalogRecord
+ ): Observable<{ user: string; date: Date }>
}
diff --git a/libs/feature/editor/src/lib/+state/editor.actions.ts b/libs/feature/editor/src/lib/+state/editor.actions.ts
index 85e02ce54e..4130e74d5c 100644
--- a/libs/feature/editor/src/lib/+state/editor.actions.ts
+++ b/libs/feature/editor/src/lib/+state/editor.actions.ts
@@ -41,3 +41,13 @@ export const setFieldVisibility = createAction(
'[Editor] Set field visibility',
props<{ field: EditorFieldIdentification; visible: boolean }>()
)
+
+export const hasRecordChangedSinceDraft = createAction(
+ '[Editor] Has Record Changed Since Draft',
+ props<{ record: CatalogRecord }>()
+)
+
+export const hasRecordChangedSinceDraftSuccess = createAction(
+ '[Editor] Has Record Changed Since Draft Success',
+ props<{ changes: { user: string; date: Date } }>()
+)
diff --git a/libs/feature/editor/src/lib/+state/editor.effects.spec.ts b/libs/feature/editor/src/lib/+state/editor.effects.spec.ts
index 5ee8316314..124cf4cf26 100644
--- a/libs/feature/editor/src/lib/+state/editor.effects.spec.ts
+++ b/libs/feature/editor/src/lib/+state/editor.effects.spec.ts
@@ -16,6 +16,7 @@ import { Gn4PlatformService } from '@geonetwork-ui/api/repository'
class EditorServiceMock {
saveRecord = jest.fn((record) => of([record, 'blabla']))
saveRecordAsDraft = jest.fn(() => of('blabla'))
+ hasRecordChangedSinceDraft = jest.fn((record) => of(['change1', 'change2']))
}
class RecordsRepositoryMock {
recordHasDraft = jest.fn(() => true)
@@ -205,4 +206,19 @@ describe('EditorEffects', () => {
})
})
})
+ describe('hasRecordChangedSinceDraft$', () => {
+ it('dispatches hasRecordChangedSinceDraftSuccess on success', () => {
+ const record = datasetRecordsFixture()[0]
+ actions = hot('-a-|', {
+ a: EditorActions.hasRecordChangedSinceDraft({ record }),
+ })
+ const expected = hot('-a-|', {
+ a: EditorActions.hasRecordChangedSinceDraftSuccess({
+ changes: ['change1', 'change2'],
+ }),
+ })
+ expect(effects.hasRecordChangedSinceDraft$).toBeObservable(expected)
+ expect(service.hasRecordChangedSinceDraft).toHaveBeenCalledWith(record)
+ })
+ })
})
diff --git a/libs/feature/editor/src/lib/+state/editor.effects.ts b/libs/feature/editor/src/lib/+state/editor.effects.ts
index cb678bf6df..4513b2e013 100644
--- a/libs/feature/editor/src/lib/+state/editor.effects.ts
+++ b/libs/feature/editor/src/lib/+state/editor.effects.ts
@@ -126,4 +126,19 @@ export class EditorEffects {
map(() => EditorActions.markRecordAsChanged())
)
)
+
+ hasRecordChangedSinceDraft$ = createEffect(() =>
+ this.actions$.pipe(
+ ofType(EditorActions.hasRecordChangedSinceDraft),
+ switchMap(({ record }) =>
+ this.editorService
+ .hasRecordChangedSinceDraft(record)
+ .pipe(
+ map((changes) =>
+ EditorActions.hasRecordChangedSinceDraftSuccess({ changes })
+ )
+ )
+ )
+ )
+ )
}
diff --git a/libs/feature/editor/src/lib/+state/editor.facade.spec.ts b/libs/feature/editor/src/lib/+state/editor.facade.spec.ts
index 663cc1b13b..e4e355056d 100644
--- a/libs/feature/editor/src/lib/+state/editor.facade.spec.ts
+++ b/libs/feature/editor/src/lib/+state/editor.facade.spec.ts
@@ -78,5 +78,12 @@ describe('EditorFacade', () => {
})
expect(spy).toHaveBeenCalledWith(action)
})
+ it('checkHasRecordChanged() should dispatch hasRecordChangedSinceDraft action', () => {
+ const spy = jest.spyOn(store, 'dispatch')
+ const record = datasetRecordsFixture()[0]
+ facade.checkHasRecordChanged(record)
+ const action = EditorActions.hasRecordChangedSinceDraft({ record })
+ expect(spy).toHaveBeenCalledWith(action)
+ })
})
})
diff --git a/libs/feature/editor/src/lib/+state/editor.facade.ts b/libs/feature/editor/src/lib/+state/editor.facade.ts
index 1b3f6dfb69..2c6e0400ee 100644
--- a/libs/feature/editor/src/lib/+state/editor.facade.ts
+++ b/libs/feature/editor/src/lib/+state/editor.facade.ts
@@ -32,6 +32,9 @@ export class EditorFacade {
draftSaveSuccess$ = this.actions$.pipe(ofType(EditorActions.draftSaveSuccess))
currentPage$ = this.store.pipe(select(EditorSelectors.selectCurrentPage))
editorConfig$ = this.store.pipe(select(EditorSelectors.selectEditorConfig))
+ hasRecordChanged$ = this.store.pipe(
+ select(EditorSelectors.selectHasRecordChanged)
+ )
openRecord(
record: CatalogRecord,
@@ -63,4 +66,8 @@ export class EditorFacade {
setFieldVisibility(field: EditorFieldIdentification, visible: boolean) {
this.store.dispatch(EditorActions.setFieldVisibility({ field, visible }))
}
+
+ checkHasRecordChanged(record: CatalogRecord) {
+ this.store.dispatch(EditorActions.hasRecordChangedSinceDraft({ record }))
+ }
}
diff --git a/libs/feature/editor/src/lib/+state/editor.reducer.spec.ts b/libs/feature/editor/src/lib/+state/editor.reducer.spec.ts
index 21585d40fc..af57bd953a 100644
--- a/libs/feature/editor/src/lib/+state/editor.reducer.spec.ts
+++ b/libs/feature/editor/src/lib/+state/editor.reducer.spec.ts
@@ -113,6 +113,18 @@ describe('Editor Reducer', () => {
expect(result.changedSinceSave).toBe(true)
})
+ it('hasRecordChangedSinceDraftSuccess action', () => {
+ const changes = ['change1', 'change2']
+ const action = EditorActions.hasRecordChangedSinceDraftSuccess({
+ changes,
+ })
+ const result: EditorState = editorReducer(
+ { ...initialEditorState, hasRecordChanged: [] },
+ action
+ )
+
+ expect(result.hasRecordChanged).toEqual(changes)
+ })
})
describe('unknown action', () => {
diff --git a/libs/feature/editor/src/lib/+state/editor.reducer.ts b/libs/feature/editor/src/lib/+state/editor.reducer.ts
index 29848bb9cd..996d6f086a 100644
--- a/libs/feature/editor/src/lib/+state/editor.reducer.ts
+++ b/libs/feature/editor/src/lib/+state/editor.reducer.ts
@@ -24,6 +24,7 @@ export interface EditorState {
changedSinceSave: boolean
editorConfig: EditorConfig
currentPage: number
+ hasRecordChanged: { user: string; date: Date }
}
export interface EditorPartialState {
@@ -39,6 +40,7 @@ export const initialEditorState: EditorState = {
changedSinceSave: false,
editorConfig: DEFAULT_CONFIGURATION,
currentPage: 0,
+ hasRecordChanged: null,
}
const reducer = createReducer(
@@ -104,6 +106,10 @@ const reducer = createReducer(
})),
})),
},
+ })),
+ on(EditorActions.hasRecordChangedSinceDraftSuccess, (state, { changes }) => ({
+ ...state,
+ hasRecordChanged: changes,
}))
)
diff --git a/libs/feature/editor/src/lib/+state/editor.selectors.spec.ts b/libs/feature/editor/src/lib/+state/editor.selectors.spec.ts
index b82f8723f2..c17eca258e 100644
--- a/libs/feature/editor/src/lib/+state/editor.selectors.spec.ts
+++ b/libs/feature/editor/src/lib/+state/editor.selectors.spec.ts
@@ -21,6 +21,7 @@ describe('Editor Selectors', () => {
saveError: 'something went wrong',
saving: false,
changedSinceSave: true,
+ hasRecordChanged: ['date', 'user'],
},
}
})
@@ -61,6 +62,11 @@ describe('Editor Selectors', () => {
expect(result).toEqual(DEFAULT_CONFIGURATION)
})
+ it('selectHasRecordChanged() should return the current "hasRecordChanged" state', () => {
+ const result = EditorSelectors.selectHasRecordChanged(state)
+ expect(result).toEqual(['date', 'user'])
+ })
+
describe('selectRecordFields', () => {
it('should return the config and value for specified page', () => {
const recordSections = EditorSelectors.selectRecordSections(state)
diff --git a/libs/feature/editor/src/lib/+state/editor.selectors.ts b/libs/feature/editor/src/lib/+state/editor.selectors.ts
index 886daa909a..528f5683b6 100644
--- a/libs/feature/editor/src/lib/+state/editor.selectors.ts
+++ b/libs/feature/editor/src/lib/+state/editor.selectors.ts
@@ -61,3 +61,8 @@ export const selectRecordSections = createSelector(
})) as EditorSectionWithValues[]
}
)
+
+export const selectHasRecordChanged = createSelector(
+ selectEditorState,
+ (state: EditorState) => state.hasRecordChanged
+)
diff --git a/libs/feature/editor/src/lib/services/editor.service.ts b/libs/feature/editor/src/lib/services/editor.service.ts
index 64d58af9bf..9d6027ccaa 100644
--- a/libs/feature/editor/src/lib/services/editor.service.ts
+++ b/libs/feature/editor/src/lib/services/editor.service.ts
@@ -59,6 +59,7 @@ export class EditorService {
record: CatalogRecord,
recordSource: string
): Observable {
+ record.recordUpdated = new Date()
return this.recordsRepository
.saveRecordAsDraft(record, recordSource)
.pipe(map(() => undefined))
@@ -70,4 +71,10 @@ export class EditorService {
this.recordsRepository.clearRecordDraft(record.uniqueIdentifier)
return this.recordsRepository.openRecordForEdition(record.uniqueIdentifier)
}
+
+ hasRecordChangedSinceDraft(
+ localRecord: CatalogRecord
+ ): Observable<{ user: string; date: Date }> {
+ return this.recordsRepository.hasRecordChangedSinceDraft(localRecord)
+ }
}
diff --git a/tools/e2e/commands.ts b/tools/e2e/commands.ts
index 27024e63cb..d2a8acea66 100644
--- a/tools/e2e/commands.ts
+++ b/tools/e2e/commands.ts
@@ -18,6 +18,7 @@ declare namespace Cypress {
clearRecordDrafts(): void
editor_readFormUniqueIdentifier(): Chainable
editor_wrapPreviousDraft(): void
+ editor_wrapFirstDraft(): void
editor_publishAndReload(): void
editor_findDraftInLocalStorage(): Chainable
@@ -185,6 +186,18 @@ Cypress.Commands.add('editor_findDraftInLocalStorage', () => {
})
})
+// this needs a recordUuid to have been wrapped
+Cypress.Commands.add('editor_wrapFirstDraft', () => {
+ cy.get('@recordUuid').then((recordUuid) => {
+ cy.window()
+ .its('localStorage')
+ .invoke('getItem', `geonetwork-ui-draft-${recordUuid}`)
+ .then((previousDraft) => {
+ cy.wrap(previousDraft).as('firstDraft')
+ })
+ })
+})
+
// this needs a recordUuid to have been wrapped
Cypress.Commands.add('editor_wrapPreviousDraft', () => {
cy.get('@recordUuid').then((recordUuid) => {
diff --git a/translations/de.json b/translations/de.json
index 3bc5c2400c..615833c769 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "Die Bedingungen sind unbekannt.",
"editor.record.form.constraint.otherConstraints": "",
"editor.record.form.constraint.securityConstraints": "",
+ "editor.record.form.draft.updateAlert": "",
"editor.record.form.field.abstract": "Kurzbeschreibung",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "",
"editor.record.placeKeywordWithoutLabel": "",
"editor.record.publish": "Diesen Datensatz veröffentlichen",
+ "editor.record.publish.confirmation.cancelText": "",
+ "editor.record.publish.confirmation.confirmText": "",
+ "editor.record.publish.confirmation.message": "",
"editor.record.publishError.body": "Der Datensatz konnte nicht veröffentlicht werden:",
"editor.record.publishError.closeMessage": "Verstanden",
"editor.record.publishError.title": "Fehler beim Veröffentlichen des Datensatzes",
diff --git a/translations/en.json b/translations/en.json
index 0b08b8caf2..f18724e436 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "The conditions are unknown.",
"editor.record.form.constraint.otherConstraints": "Other constraints",
"editor.record.form.constraint.securityConstraints": "Security constraints",
+ "editor.record.form.draft.updateAlert": "Since you created this draft, the record has been updated on { date } by { user }. Publishing your draft might erase their edits. To avoid this, you need to either cancel your changes or knowingly publish your own version.",
"editor.record.form.field.abstract": "Abstract",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "Please provide at least one point of contact.",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "Error adding resource",
"editor.record.placeKeywordWithoutLabel": "Unnamed location",
"editor.record.publish": "Publish this record",
+ "editor.record.publish.confirmation.cancelText": "Cancel",
+ "editor.record.publish.confirmation.confirmText": "Publish",
+ "editor.record.publish.confirmation.message": "Since you created this draft, the record has been updated on { date } by { user }. Publishing your draft might erase their edits. Do you wish to proceed ?",
"editor.record.publishError.body": "The record could not be published:",
"editor.record.publishError.closeMessage": "Understood",
"editor.record.publishError.title": "Error publishing record",
diff --git a/translations/es.json b/translations/es.json
index 6292a8e6d0..1bed5d65f9 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "",
"editor.record.form.constraint.otherConstraints": "",
"editor.record.form.constraint.securityConstraints": "",
+ "editor.record.form.draft.updateAlert": "",
"editor.record.form.field.abstract": "",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "",
"editor.record.placeKeywordWithoutLabel": "",
"editor.record.publish": "",
+ "editor.record.publish.confirmation.cancelText": "",
+ "editor.record.publish.confirmation.confirmText": "",
+ "editor.record.publish.confirmation.message": "",
"editor.record.publishError.body": "",
"editor.record.publishError.closeMessage": "",
"editor.record.publishError.title": "",
diff --git a/translations/fr.json b/translations/fr.json
index 16d398c314..264d2c0310 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "Les conditions sont inconnues.",
"editor.record.form.constraint.otherConstraints": "Autres contraintes",
"editor.record.form.constraint.securityConstraints": "Contraintes de sécurité",
+ "editor.record.form.draft.updateAlert": "Depuis la création de ce brouillon, cette fiche a été modifiée le { date } par { user }. Publier votre version peut supprimer ses modifications. Pour éviter cela, vous pouvez annuler vos changements, ou publier votre version en connaissance de cause.",
"editor.record.form.field.abstract": "Résumé",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "Veuillez renseigner au moins un point de contact.",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "Erreur lors de l'ajout d'une ressource",
"editor.record.placeKeywordWithoutLabel": "Localisation sans nom",
"editor.record.publish": "Publier cette fiche",
+ "editor.record.publish.confirmation.cancelText": "Annuler",
+ "editor.record.publish.confirmation.confirmText": "Publier",
+ "editor.record.publish.confirmation.message": "Depuis la création de votre brouillon, cette fiche a été modifiée le { date } par { user }. Publier votre version pourrait supprimer ses modifications. Souhaitez-vous poursuivre ?",
"editor.record.publishError.body": "La fiche n'a pas pu être publiée :",
"editor.record.publishError.closeMessage": "Compris",
"editor.record.publishError.title": "Erreur lors de la publication",
diff --git a/translations/it.json b/translations/it.json
index 2256e7d942..19217bb533 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "",
"editor.record.form.constraint.otherConstraints": "",
"editor.record.form.constraint.securityConstraints": "",
+ "editor.record.form.draft.updateAlert": "",
"editor.record.form.field.abstract": "",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "",
"editor.record.placeKeywordWithoutLabel": "",
"editor.record.publish": "",
+ "editor.record.publish.confirmation.cancelText": "",
+ "editor.record.publish.confirmation.confirmText": "",
+ "editor.record.publish.confirmation.message": "",
"editor.record.publishError.body": "",
"editor.record.publishError.closeMessage": "",
"editor.record.publishError.title": "",
diff --git a/translations/nl.json b/translations/nl.json
index 8ce69d2469..8a459ff95a 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "",
"editor.record.form.constraint.otherConstraints": "",
"editor.record.form.constraint.securityConstraints": "",
+ "editor.record.form.draft.updateAlert": "",
"editor.record.form.field.abstract": "",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "",
"editor.record.placeKeywordWithoutLabel": "",
"editor.record.publish": "",
+ "editor.record.publish.confirmation.cancelText": "",
+ "editor.record.publish.confirmation.confirmText": "",
+ "editor.record.publish.confirmation.message": "",
"editor.record.publishError.body": "",
"editor.record.publishError.closeMessage": "",
"editor.record.publishError.title": "",
diff --git a/translations/pt.json b/translations/pt.json
index 1fd3657736..544401e616 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "",
"editor.record.form.constraint.otherConstraints": "",
"editor.record.form.constraint.securityConstraints": "",
+ "editor.record.form.draft.updateAlert": "",
"editor.record.form.field.abstract": "",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "",
"editor.record.placeKeywordWithoutLabel": "",
"editor.record.publish": "",
+ "editor.record.publish.confirmation.cancelText": "",
+ "editor.record.publish.confirmation.confirmText": "",
+ "editor.record.publish.confirmation.message": "",
"editor.record.publishError.body": "",
"editor.record.publishError.closeMessage": "",
"editor.record.publishError.title": "",
diff --git a/translations/sk.json b/translations/sk.json
index 070a188a9e..5ecf1c88d0 100644
--- a/translations/sk.json
+++ b/translations/sk.json
@@ -211,6 +211,7 @@
"editor.record.form.constraint.not.known": "",
"editor.record.form.constraint.otherConstraints": "",
"editor.record.form.constraint.securityConstraints": "",
+ "editor.record.form.draft.updateAlert": "",
"editor.record.form.field.abstract": "",
"editor.record.form.field.constraintsShortcuts": "",
"editor.record.form.field.contacts.noContact": "",
@@ -287,6 +288,9 @@
"editor.record.onlineResourceError.title": "",
"editor.record.placeKeywordWithoutLabel": "",
"editor.record.publish": "",
+ "editor.record.publish.confirmation.cancelText": "",
+ "editor.record.publish.confirmation.confirmText": "",
+ "editor.record.publish.confirmation.message": "",
"editor.record.publishError.body": "",
"editor.record.publishError.closeMessage": "",
"editor.record.publishError.title": "",