diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component.html index 9f74216d54..aee9fb980c 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component.html @@ -3,6 +3,7 @@ -
+
{{ mdValue.newValue.value }}
- + + + + +
+ + + {{ dsoType + '.edit.metadata.authority.label' | translate }} {{ mdValue.newValue.authority }} + +
+
+
+ + + +
+
{{ mdRepresentationName$ | async }} @@ -45,14 +85,14 @@ [disabled]="isVirtual || (!mdValue.change && mdValue.reordered) || (!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="undo.emit()"> +
-
diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts index 459095ea67..080a22dd7c 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts @@ -11,6 +11,18 @@ import { ItemMetadataRepresentation } from '../../../core/shared/metadata-repres import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../../../core/shared/metadata.models'; import { DsoEditMetadataChangeType, DsoEditMetadataValue } from '../dso-edit-metadata-form'; import { By } from '@angular/platform-browser'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { Item } from '../../../core/shared/item.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { Collection } from '../../../core/shared/collection.model'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { Vocabulary } from 'src/app/core/submission/vocabularies/models/vocabulary.model'; +import { VocabularyServiceStub } from 'src/app/shared/testing/vocabulary-service.stub'; +import { VocabularyService } from 'src/app/core/submission/vocabularies/vocabulary.service'; +import { ConfidenceType } from 'src/app/core/shared/confidence-type'; +import { DynamicOneboxModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.model'; +import { Observable } from 'rxjs'; +import { DynamicScrollableDropdownModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; const EDIT_BTN = 'edit'; const CONFIRM_BTN = 'confirm'; @@ -24,9 +36,76 @@ describe('DsoEditMetadataValueComponent', () => { let relationshipService: RelationshipDataService; let dsoNameService: DSONameService; + let vocabularyServiceStub: any; + let itemService: ItemDataService; let editMetadataValue: DsoEditMetadataValue; let metadataValue: MetadataValue; + let dso: DSpaceObject; + + const collection = Object.assign(new Collection(), { + uuid: 'fake-uuid' + }); + + const item = Object.assign(new Item(), { + _links: { + self: { href: 'fake-item-url/item' } + }, + id: 'item', + uuid: 'item', + owningCollection: createSuccessfulRemoteDataObject$(collection) + }); + + const mockVocabularyScrollable: Vocabulary = { + id: 'scrollable', + name: 'scrollable', + scrollable: true, + hierarchical: false, + preloadLevel: 0, + type: 'vocabulary', + _links: { + self: { + href: 'self' + }, + entries: { + href: 'entries' + } + } + }; + + const mockVocabularyHierarchical: Vocabulary = { + id: 'hierarchical', + name: 'hierarchical', + scrollable: false, + hierarchical: true, + preloadLevel: 2, + type: 'vocabulary', + _links: { + self: { + href: 'self' + }, + entries: { + href: 'entries' + } + } + }; + + const mockVocabularySuggester: Vocabulary = { + id: 'suggester', + name: 'suggester', + scrollable: false, + hierarchical: false, + preloadLevel: 0, + type: 'vocabulary', + _links: { + self: { + href: 'self' + }, + entries: { + href: 'entries' + } + } + }; function initServices(): void { relationshipService = jasmine.createSpyObj('relationshipService', { @@ -35,6 +114,10 @@ describe('DsoEditMetadataValueComponent', () => { dsoNameService = jasmine.createSpyObj('dsoNameService', { getName: 'Related Name', }); + itemService = jasmine.createSpyObj('itemService', { + findByHref: createSuccessfulRemoteDataObject$(item) + }); + vocabularyServiceStub = new VocabularyServiceStub(); } beforeEach(waitForAsync(() => { @@ -45,6 +128,11 @@ describe('DsoEditMetadataValueComponent', () => { authority: undefined, }); editMetadataValue = new DsoEditMetadataValue(metadataValue); + dso = Object.assign(new DSpaceObject(), { + _links: { + self: { href: 'fake-dso-url/dso' } + }, + }); initServices(); @@ -54,6 +142,8 @@ describe('DsoEditMetadataValueComponent', () => { providers: [ { provide: RelationshipDataService, useValue: relationshipService }, { provide: DSONameService, useValue: dsoNameService }, + { provide: VocabularyService, useValue: vocabularyServiceStub }, + { provide: ItemDataService, useValue: itemService } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -63,6 +153,7 @@ describe('DsoEditMetadataValueComponent', () => { fixture = TestBed.createComponent(DsoEditMetadataValueComponent); component = fixture.componentInstance; component.mdValue = editMetadataValue; + component.dso = dso; component.saving$ = of(false); fixture.detectChanges(); }); @@ -144,6 +235,222 @@ describe('DsoEditMetadataValueComponent', () => { assertButton(DRAG_BTN, true, false); }); + describe('when the metadata field not uses a vocabulary and is editing', () => { + beforeEach(waitForAsync(() => { + spyOn(vocabularyServiceStub, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(null, 204)); + metadataValue = Object.assign(new MetadataValue(), { + value: 'Regular value', + language: 'en', + place: 0, + authority: null, + }); + editMetadataValue = new DsoEditMetadataValue(metadataValue); + editMetadataValue.editing = true; + component.mdValue = editMetadataValue; + component.mdField = 'metadata.regular'; + component.ngOnInit(); + fixture.detectChanges(); + })); + + it('should render a textarea', () => { + expect(vocabularyServiceStub.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); + expect(fixture.debugElement.query(By.css('textarea'))).toBeTruthy(); + }); + }); + + describe('when the metadata field uses a scrollable vocabulary and is editing', () => { + beforeEach(waitForAsync(() => { + spyOn(vocabularyServiceStub, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(mockVocabularyScrollable)); + metadataValue = Object.assign(new MetadataValue(), { + value: 'Authority Controlled value', + language: 'en', + place: 0, + authority: null, + }); + editMetadataValue = new DsoEditMetadataValue(metadataValue); + editMetadataValue.editing = true; + component.mdValue = editMetadataValue; + component.mdField = 'metadata.scrollable'; + component.ngOnInit(); + fixture.detectChanges(); + })); + + it('should render the DsDynamicScrollableDropdownComponent', () => { + expect(vocabularyServiceStub.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); + expect(fixture.debugElement.query(By.css('ds-dynamic-scrollable-dropdown'))).toBeTruthy(); + }); + + it('getModel should return a DynamicScrollableDropdownModel', () => { + const result = component.getModel(); + + expect(result instanceof Observable).toBe(true); + + result.subscribe((model) => { + expect(model instanceof DynamicScrollableDropdownModel).toBe(true); + expect(model.vocabularyOptions.name).toBe(mockVocabularyScrollable.name); + }); + }); + }); + + describe('when the metadata field uses a hierarchical vocabulary and is editing', () => { + beforeEach(waitForAsync(() => { + spyOn(vocabularyServiceStub, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(mockVocabularyHierarchical)); + metadataValue = Object.assign(new MetadataValue(), { + value: 'Authority Controlled value', + language: 'en', + place: 0, + authority: null, + }); + editMetadataValue = new DsoEditMetadataValue(metadataValue); + editMetadataValue.editing = true; + component.mdValue = editMetadataValue; + component.mdField = 'metadata.hierarchical'; + component.ngOnInit(); + fixture.detectChanges(); + })); + + it('should render the DsDynamicOneboxComponent', () => { + expect(vocabularyServiceStub.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); + expect(fixture.debugElement.query(By.css('ds-dynamic-onebox'))).toBeTruthy(); + }); + + it('getModel should return a DynamicOneboxModel', () => { + const result = component.getModel(); + + expect(result instanceof Observable).toBe(true); + + result.subscribe((model) => { + expect(model instanceof DynamicOneboxModel).toBe(true); + expect(model.vocabularyOptions.name).toBe(mockVocabularyHierarchical.name); + }); + }); + }); + + describe('when the metadata field uses a suggester vocabulary and is editing', () => { + beforeEach(waitForAsync(() => { + spyOn(vocabularyServiceStub, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(mockVocabularySuggester)); + spyOn(component.confirm, 'emit'); + metadataValue = Object.assign(new MetadataValue(), { + value: 'Authority Controlled value', + language: 'en', + place: 0, + authority: 'authority-key', + confidence: ConfidenceType.CF_UNCERTAIN + }); + editMetadataValue = new DsoEditMetadataValue(metadataValue); + editMetadataValue.editing = true; + component.mdValue = editMetadataValue; + component.mdField = 'metadata.suggester'; + component.ngOnInit(); + fixture.detectChanges(); + })); + + it('should render the DsDynamicOneboxComponent', () => { + expect(vocabularyServiceStub.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); + expect(fixture.debugElement.query(By.css('ds-dynamic-onebox'))).toBeTruthy(); + }); + + it('getModel should return a DynamicOneboxModel', () => { + const result = component.getModel(); + + expect(result instanceof Observable).toBe(true); + + result.subscribe((model) => { + expect(model instanceof DynamicOneboxModel).toBe(true); + expect(model.vocabularyOptions.name).toBe(mockVocabularySuggester.name); + }); + }); + + describe('authority key edition', () => { + + it('should update confidence to CF_NOVALUE when authority is cleared', () => { + component.mdValue.newValue.authority = ''; + + component.onChangeAuthorityKey(); + + expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_NOVALUE); + expect(component.confirm.emit).toHaveBeenCalledWith(false); + }); + + it('should update confidence to CF_ACCEPTED when authority key is edited', () => { + component.mdValue.newValue.authority = 'newAuthority'; + component.mdValue.originalValue.authority = 'oldAuthority'; + + component.onChangeAuthorityKey(); + + expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_ACCEPTED); + expect(component.confirm.emit).toHaveBeenCalledWith(false); + }); + + it('should not update confidence when authority key remains the same', () => { + component.mdValue.newValue.authority = 'sameAuthority'; + component.mdValue.originalValue.authority = 'sameAuthority'; + + component.onChangeAuthorityKey(); + + expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_UNCERTAIN); + expect(component.confirm.emit).not.toHaveBeenCalled(); + }); + + it('should call onChangeEditingAuthorityStatus with true when clicking the lock button', () => { + spyOn(component, 'onChangeEditingAuthorityStatus'); + const lockButton = fixture.nativeElement.querySelector('#metadata-confirm-btn'); + + lockButton.click(); + + expect(component.onChangeEditingAuthorityStatus).toHaveBeenCalledWith(true); + }); + + it('should disable the input when editingAuthority is false', () => { + component.editingAuthority = false; + + fixture.detectChanges(); + + const inputElement = fixture.nativeElement.querySelector('input'); + expect(inputElement.disabled).toBe(true); + }); + + it('should enable the input when editingAuthority is true', () => { + component.editingAuthority = true; + + fixture.detectChanges(); + + const inputElement = fixture.nativeElement.querySelector('input'); + expect(inputElement.disabled).toBe(false); + }); + + it('should update mdValue.newValue properties when authority is present', () => { + const event = { + value: 'Some value', + authority: 'Some authority', + }; + + component.onChangeAuthorityField(event); + + expect(component.mdValue.newValue.value).toBe(event.value); + expect(component.mdValue.newValue.authority).toBe(event.authority); + expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_ACCEPTED); + expect(component.confirm.emit).toHaveBeenCalledWith(false); + }); + + it('should update mdValue.newValue properties when authority is not present', () => { + const event = { + value: 'Some value', + authority: null, + }; + + component.onChangeAuthorityField(event); + + expect(component.mdValue.newValue.value).toBe(event.value); + expect(component.mdValue.newValue.authority).toBeNull(); + expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_UNSET); + expect(component.confirm.emit).toHaveBeenCalledWith(false); + }); + + }); + + }); + function assertButton(name: string, exists: boolean, disabled: boolean = false): void { describe(`${name} button`, () => { let btn: DebugElement; diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts index 3fdcd381ab..578d4c3c44 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts @@ -8,10 +8,25 @@ import { import { RelationshipDataService } from '../../../core/data/relationship-data.service'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { EMPTY } from 'rxjs/internal/observable/empty'; +import { VocabularyService } from 'src/app/core/submission/vocabularies/vocabulary.service'; +import { Vocabulary } from '../../../core/submission/vocabularies/models/vocabulary.model'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { ConfidenceType } from '../../../core/shared/confidence-type'; +import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload, getRemoteDataPayload } from '../../../core/shared/operators'; +import { DsDynamicOneboxModelConfig, DynamicOneboxModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.model'; +import { DynamicScrollableDropdownModel, DynamicScrollableDropdownModelConfig } from '../../../shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { followLink } from '../../../shared/utils/follow-link-config.model'; +import { Item } from '../../../core/shared/item.model'; +import { Collection } from '../../../core/shared/collection.model'; +import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; +import { isNotEmpty } from '../../../shared/empty.util'; +import { of as observableOf } from 'rxjs'; @Component({ selector: 'ds-dso-edit-metadata-value', @@ -51,6 +66,11 @@ export class DsoEditMetadataValueComponent implements OnInit { */ @Input() isOnlyValue = false; + /** + * MetadataField to edit + */ + @Input() mdField?: string; + /** * Emits when the user clicked edit */ @@ -82,6 +102,12 @@ export class DsoEditMetadataValueComponent implements OnInit { */ public DsoEditMetadataChangeTypeEnum = DsoEditMetadataChangeType; + /** + * The ConfidenceType enumeration for access in the component's template + * @type {ConfidenceType} + */ + public ConfidenceTypeEnum = ConfidenceType; + /** * The item this metadata value represents in case it's virtual (if any, otherwise null) */ @@ -97,12 +123,44 @@ export class DsoEditMetadataValueComponent implements OnInit { */ mdRepresentationName$: Observable; + /** + * Whether or not the authority field is currently being edited + */ + public editingAuthority = false; + + /** + * Field group used by authority field + * @type {UntypedFormGroup} + */ + group = new UntypedFormGroup({ authorityField : new UntypedFormControl()}); + + /** + * Observable property of the model to use for editinf authorities values + */ + private model$: Observable; + + /** + * Observable with information about the authority vocabulary used + */ + private vocabulary$: Observable; + + /** + * Observables with information about the authority vocabulary type used + */ + private isAuthorityControlled$: Observable; + private isHierarchicalVocabulary$: Observable; + private isScrollableVocabulary$: Observable; + private isSuggesterVocabulary$: Observable; + constructor(protected relationshipService: RelationshipDataService, - protected dsoNameService: DSONameService) { + protected dsoNameService: DSONameService, + protected vocabularyService: VocabularyService, + protected itemService: ItemDataService) { } ngOnInit(): void { this.initVirtualProperties(); + this.initAuthorityProperties(); } /** @@ -123,4 +181,173 @@ export class DsoEditMetadataValueComponent implements OnInit { map((mdRepresentation: ItemMetadataRepresentation) => mdRepresentation ? this.dsoNameService.getName(mdRepresentation) : null), ); } + + /** + * Initialise potential properties of a authority controlled metadata field + */ + initAuthorityProperties(): void { + + if (isNotEmpty(this.mdField)) { + + const owningCollection$: Observable = this.itemService.findByHref(this.dso._links.self.href, true, true, followLink('owningCollection')) + .pipe( + getFirstSucceededRemoteData(), + getRemoteDataPayload(), + switchMap((item: Item) => item.owningCollection), + getFirstSucceededRemoteData(), + getRemoteDataPayload() + ); + + this.vocabulary$ = owningCollection$.pipe( + switchMap((c: Collection) => this.vocabularyService + .getVocabularyByMetadataAndCollection(this.mdField, c.uuid) + .pipe( + getFirstSucceededRemoteDataPayload() + )) + ); + } else { + this.vocabulary$ = observableOf(undefined); + } + + this.isAuthorityControlled$ = this.vocabulary$.pipe( + map((result: Vocabulary) => isNotEmpty(result)) + ); + + this.isHierarchicalVocabulary$ = this.vocabulary$.pipe( + map((result: Vocabulary) => isNotEmpty(result) && result.hierarchical) + ); + + this.isScrollableVocabulary$ = this.vocabulary$.pipe( + map((result: Vocabulary) => isNotEmpty(result) && result.scrollable) + ); + + this.isSuggesterVocabulary$ = this.vocabulary$.pipe( + map((result: Vocabulary) => isNotEmpty(result) && !result.hierarchical && !result.scrollable) + ); + + this.model$ = this.vocabulary$.pipe( + map((vocabulary: Vocabulary) => { + let formFieldValue; + if (isNotEmpty(this.mdValue.newValue.value)) { + formFieldValue = new FormFieldMetadataValueObject(); + formFieldValue.value = this.mdValue.newValue.value; + formFieldValue.display = this.mdValue.newValue.value; + if (this.mdValue.newValue.authority) { + formFieldValue.authority = this.mdValue.newValue.authority; + formFieldValue.confidence = this.mdValue.newValue.confidence; + } + } else { + formFieldValue = this.mdValue.newValue.value; + } + + let vocabularyOptions = vocabulary ? { + closed: false, + name: vocabulary.name + } as VocabularyOptions : null; + + if (!vocabulary.scrollable) { + let model: DsDynamicOneboxModelConfig = { + id: 'authorityField', + label: `${this.dsoType}.edit.metadata.edit.value`, + vocabularyOptions: vocabularyOptions, + metadataFields: [this.mdField], + value: formFieldValue, + repeatable: false, + submissionId: 'edit-metadata', + hasSelectableMetadata: false, + }; + return new DynamicOneboxModel(model); + } else { + let model: DynamicScrollableDropdownModelConfig = { + id: 'authorityField', + label: `${this.dsoType}.edit.metadata.edit.value`, + placeholder: `${this.dsoType}.edit.metadata.edit.value`, + vocabularyOptions: vocabularyOptions, + metadataFields: [this.mdField], + value: formFieldValue, + repeatable: false, + submissionId: 'edit-metadata', + hasSelectableMetadata: false, + maxOptions: 10 + }; + return new DynamicScrollableDropdownModel(model); + } + })); + } + + /** + * Checks if this field use a authority vocabulary + */ + isAuthorityControlled(): Observable { + return this.isAuthorityControlled$; + } + + /** + * Checks if configured vocabulary is Hierarchical or not + */ + isHierarchicalVocabulary(): Observable { + return this.isHierarchicalVocabulary$; + } + + /** + * Checks if configured vocabulary is Scrollable or not + */ + isScrollableVocabulary(): Observable { + return this.isScrollableVocabulary$; + } + + /** + * Checks if configured vocabulary is Suggester or not + * (a vocabulary not Scrollable and not Hierarchical that uses an autocomplete field) + */ + isSuggesterVocabulary(): Observable { + return this.isSuggesterVocabulary$; + } + + /** + * Process the change of authority field value updating the authority key and confidence as necessary + */ + onChangeAuthorityField(event): void { + this.mdValue.newValue.value = event.value; + if (event.authority) { + this.mdValue.newValue.authority = event.authority; + this.mdValue.newValue.confidence = ConfidenceType.CF_ACCEPTED; + } else { + this.mdValue.newValue.authority = null; + this.mdValue.newValue.confidence = ConfidenceType.CF_UNSET; + } + this.confirm.emit(false); + } + + /** + * Returns an observable with the {@link DynamicOneboxModel} or {@link DynamicScrollableDropdownModel} model used + * for the authority field + */ + getModel(): Observable { + return this.model$; + } + + /** + * Change the status of the editingAuthority property + * @param status + */ + onChangeEditingAuthorityStatus(status: boolean) { + this.editingAuthority = status; + } + + /** + * Processes the change in authority value, updating the confidence as necessary. + * If the authority key is cleared, the confidence is set to {@link ConfidenceType.CF_NOVALUE}. + * If the authority key is edited and differs from the original, the confidence is set to {@link ConfidenceType.CF_ACCEPTED}. + */ + onChangeAuthorityKey() { + if (this.mdValue.newValue.authority === '') { + this.mdValue.newValue.confidence = ConfidenceType.CF_NOVALUE; + this.confirm.emit(false); + } else if (this.mdValue.newValue.authority !== this.mdValue.originalValue.authority) { + this.mdValue.newValue.confidence = ConfidenceType.CF_ACCEPTED; + this.confirm.emit(false); + } + } + } diff --git a/src/app/dso-shared/dso-shared.module.ts b/src/app/dso-shared/dso-shared.module.ts index 7d44d6a920..47a94c3de8 100644 --- a/src/app/dso-shared/dso-shared.module.ts +++ b/src/app/dso-shared/dso-shared.module.ts @@ -7,10 +7,12 @@ import { DsoEditMetadataValueComponent } from './dso-edit-metadata/dso-edit-meta import { DsoEditMetadataHeadersComponent } from './dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component'; import { DsoEditMetadataValueHeadersComponent } from './dso-edit-metadata/dso-edit-metadata-value-headers/dso-edit-metadata-value-headers.component'; import { ThemedDsoEditMetadataComponent } from './dso-edit-metadata/themed-dso-edit-metadata.component'; +import { FormModule } from '../shared/form/form.module'; @NgModule({ imports: [ SharedModule, + FormModule ], declarations: [ DsoEditMetadataComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index c75336e907..6e9f924c66 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2152,6 +2152,8 @@ "item.edit.metadata.edit.value": "Edit value", + "item.edit.metadata.edit.authority.key": "Edit authority key", + "item.edit.metadata.edit.buttons.confirm": "Confirm", "item.edit.metadata.edit.buttons.drag": "Drag to reorder", @@ -2206,6 +2208,12 @@ "item.edit.metadata.save-button": "Save", + "item.edit.metadata.authority.label": "Authority: ", + + "item.edit.metadata.edit.buttons.open-authority-edition": "Unlock the authority key value for manual editing", + + "item.edit.metadata.edit.buttons.close-authority-edition": "Lock the authority key value for manual editing", + "item.edit.modify.overview.field": "Field", "item.edit.modify.overview.language": "Language",