mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Implementation of controlled vocabularies value selectors during item editing
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
<ds-dso-edit-metadata-value *ngFor="let mdValue of form.fields[mdField]; let idx = index" role="presentation"
|
<ds-dso-edit-metadata-value *ngFor="let mdValue of form.fields[mdField]; let idx = index" role="presentation"
|
||||||
[dso]="dso"
|
[dso]="dso"
|
||||||
[mdValue]="mdValue"
|
[mdValue]="mdValue"
|
||||||
|
[mdField]="mdField"
|
||||||
[dsoType]="dsoType"
|
[dsoType]="dsoType"
|
||||||
[saving$]="saving$"
|
[saving$]="saving$"
|
||||||
[isOnlyValue]="form.fields[mdField].length === 1"
|
[isOnlyValue]="form.fields[mdField].length === 1"
|
||||||
|
@@ -75,7 +75,8 @@ export class DsoEditMetadataValue {
|
|||||||
confirmChanges(finishEditing = false) {
|
confirmChanges(finishEditing = false) {
|
||||||
this.reordered = this.originalValue.place !== this.newValue.place;
|
this.reordered = this.originalValue.place !== this.newValue.place;
|
||||||
if (hasNoValue(this.change) || this.change === DsoEditMetadataChangeType.UPDATE) {
|
if (hasNoValue(this.change) || this.change === DsoEditMetadataChangeType.UPDATE) {
|
||||||
if ((this.originalValue.value !== this.newValue.value || this.originalValue.language !== this.newValue.language)) {
|
if (this.originalValue.value !== this.newValue.value || this.originalValue.language !== this.newValue.language
|
||||||
|
|| this.originalValue.authority !== this.newValue.authority || this.originalValue.confidence !== this.newValue.confidence) {
|
||||||
this.change = DsoEditMetadataChangeType.UPDATE;
|
this.change = DsoEditMetadataChangeType.UPDATE;
|
||||||
} else {
|
} else {
|
||||||
this.change = undefined;
|
this.change = undefined;
|
||||||
@@ -404,10 +405,13 @@ export class DsoEditMetadataForm {
|
|||||||
if (hasValue(value.change)) {
|
if (hasValue(value.change)) {
|
||||||
if (value.change === DsoEditMetadataChangeType.UPDATE) {
|
if (value.change === DsoEditMetadataChangeType.UPDATE) {
|
||||||
// Only changes to value or language are considered "replace" operations. Changes to place are considered "move", which is processed below.
|
// Only changes to value or language are considered "replace" operations. Changes to place are considered "move", which is processed below.
|
||||||
if (value.originalValue.value !== value.newValue.value || value.originalValue.language !== value.newValue.language) {
|
if (value.originalValue.value !== value.newValue.value || value.originalValue.language !== value.newValue.language
|
||||||
|
|| value.originalValue.authority !== value.newValue.authority || value.originalValue.confidence !== value.newValue.confidence) {
|
||||||
replaceOperations.push(new MetadataPatchReplaceOperation(field, value.originalValue.place, {
|
replaceOperations.push(new MetadataPatchReplaceOperation(field, value.originalValue.place, {
|
||||||
value: value.newValue.value,
|
value: value.newValue.value,
|
||||||
language: value.newValue.language,
|
language: value.newValue.language,
|
||||||
|
authority: value.newValue.authority,
|
||||||
|
confidence: value.newValue.confidence
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else if (value.change === DsoEditMetadataChangeType.REMOVE) {
|
} else if (value.change === DsoEditMetadataChangeType.REMOVE) {
|
||||||
@@ -416,6 +420,8 @@ export class DsoEditMetadataForm {
|
|||||||
addOperations.push(new MetadataPatchAddOperation(field, {
|
addOperations.push(new MetadataPatchAddOperation(field, {
|
||||||
value: value.newValue.value,
|
value: value.newValue.value,
|
||||||
language: value.newValue.language,
|
language: value.newValue.language,
|
||||||
|
authority: value.newValue.authority,
|
||||||
|
confidence: value.newValue.confidence
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
console.warn('Illegal metadata change state detected for', value);
|
console.warn('Illegal metadata change state detected for', value);
|
||||||
|
@@ -1,11 +1,51 @@
|
|||||||
<div class="d-flex flex-row ds-value-row" *ngVar="mdValue.newValue.isVirtual as isVirtual" role="row"
|
<div class="d-flex flex-row ds-value-row" *ngVar="mdValue.newValue.isVirtual as isVirtual" role="row"
|
||||||
cdkDrag (cdkDragStarted)="dragging.emit(true)" (cdkDragEnded)="dragging.emit(false)"
|
cdkDrag (cdkDragStarted)="dragging.emit(true)" (cdkDragEnded)="dragging.emit(false)"
|
||||||
[ngClass]="{ 'ds-warning': mdValue.reordered || mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': isOnlyValue }">
|
[ngClass]="{ 'ds-warning': mdValue.reordered || mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': isOnlyValue }">
|
||||||
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell">
|
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex flex-column" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell">
|
||||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing && !mdRepresentation">{{ mdValue.newValue.value }}</div>
|
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing && !mdRepresentation">{{ mdValue.newValue.value }}</div>
|
||||||
<textarea class="form-control" rows="5" *ngIf="mdValue.editing && !mdRepresentation" [(ngModel)]="mdValue.newValue.value"
|
<textarea class="form-control" rows="5" *ngIf="mdValue.editing && !mdRepresentation && !(isAuthorityControlled() | async)" [(ngModel)]="mdValue.newValue.value"
|
||||||
[attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate"
|
[attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate"
|
||||||
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
|
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
|
||||||
|
<ds-dynamic-scrollable-dropdown *ngIf="mdValue.editing && (isScrollableVocabulary() | async)"
|
||||||
|
[bindId]="mdField"
|
||||||
|
[group]="group"
|
||||||
|
[model]="getModel() | async"
|
||||||
|
(change)="onChangeAuthorityField($event)">
|
||||||
|
</ds-dynamic-scrollable-dropdown>
|
||||||
|
<ds-dynamic-onebox *ngIf="mdValue.editing && ((isHierarchicalVocabulary() | async) || (isSuggesterVocabulary() | async))"
|
||||||
|
[group]="group"
|
||||||
|
[model]="getModel() | async"
|
||||||
|
(change)="onChangeAuthorityField($event)">
|
||||||
|
</ds-dynamic-onebox>
|
||||||
|
<div *ngIf="!isVirtual && !mdValue.editing && mdValue.newValue.authority">
|
||||||
|
<span class="badge badge-light border" >
|
||||||
|
<i dsAuthorityConfidenceState
|
||||||
|
class="far fa-circle fa-fw p-0"
|
||||||
|
aria-hidden="true"
|
||||||
|
[authorityValue]="mdValue.newValue"
|
||||||
|
></i>
|
||||||
|
{{ dsoType + '.edit.metadata.authority.label' | translate }} {{ mdValue.newValue.authority }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2" *ngIf=" mdValue.editing && (isAuthorityControlled() | async) && (isSuggesterVocabulary() | async)">
|
||||||
|
<div class="btn-group w-75">
|
||||||
|
<input class="form-control form-outline" [(ngModel)]="mdValue.newValue.authority" [disabled]="!editingAuthority"
|
||||||
|
[attr.aria-label]="(dsoType + '.edit.metadata.edit.authority.key') | translate"
|
||||||
|
(change)="onChangeAuthorityKey()" />
|
||||||
|
<button class="btn btn-outline-secondary btn-sm ng-star-inserted" id="metadata-confirm-btn" *ngIf="!editingAuthority"
|
||||||
|
[title]="dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate"
|
||||||
|
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate }}"
|
||||||
|
(click)="onChangeEditingAuthorityStatus(true)">
|
||||||
|
<i class="fas fa-lock fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-success btn-sm ng-star-inserted" id="metadata-confirm-btn" *ngIf="editingAuthority"
|
||||||
|
[title]="dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate"
|
||||||
|
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate }}"
|
||||||
|
(click)="onChangeEditingAuthorityStatus(false)">
|
||||||
|
<i class="fas fa-lock-open fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="d-flex" *ngIf="mdRepresentation">
|
<div class="d-flex" *ngIf="mdRepresentation">
|
||||||
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
||||||
<ds-themed-type-badge [object]="mdRepresentation"></ds-themed-type-badge>
|
<ds-themed-type-badge [object]="mdRepresentation"></ds-themed-type-badge>
|
||||||
@@ -45,14 +85,14 @@
|
|||||||
[disabled]="isVirtual || (!mdValue.change && mdValue.reordered) || (!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="undo.emit()">
|
[disabled]="isVirtual || (!mdValue.change && mdValue.reordered) || (!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="undo.emit()">
|
||||||
<i class="fas fa-undo-alt fa-fw"></i>
|
<i class="fas fa-undo-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary ds-drag-handle btn-sm" data-test="metadata-drag-btn" *ngVar="(isOnlyValue || (saving$ | async)) as disabled"
|
||||||
|
cdkDragHandle [cdkDragHandleDisabled]="disabled" [ngClass]="{'disabled': disabled}" [disabled]="disabled"
|
||||||
|
[title]="dsoType + '.edit.metadata.edit.buttons.drag' | translate"
|
||||||
|
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}">
|
||||||
|
<i class="fas fa-grip-vertical fa-fw"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-outline-secondary ds-drag-handle btn-sm" data-test="metadata-drag-btn" *ngVar="(isOnlyValue || (saving$ | async)) as disabled"
|
|
||||||
cdkDragHandle [cdkDragHandleDisabled]="disabled" [ngClass]="{'disabled': disabled}" [disabled]="disabled"
|
|
||||||
[title]="dsoType + '.edit.metadata.edit.buttons.drag' | translate"
|
|
||||||
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}">
|
|
||||||
<i class="fas fa-grip-vertical fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -11,6 +11,18 @@ import { ItemMetadataRepresentation } from '../../../core/shared/metadata-repres
|
|||||||
import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../../../core/shared/metadata.models';
|
import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../../../core/shared/metadata.models';
|
||||||
import { DsoEditMetadataChangeType, DsoEditMetadataValue } from '../dso-edit-metadata-form';
|
import { DsoEditMetadataChangeType, DsoEditMetadataValue } from '../dso-edit-metadata-form';
|
||||||
import { By } from '@angular/platform-browser';
|
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 EDIT_BTN = 'edit';
|
||||||
const CONFIRM_BTN = 'confirm';
|
const CONFIRM_BTN = 'confirm';
|
||||||
@@ -24,9 +36,76 @@ describe('DsoEditMetadataValueComponent', () => {
|
|||||||
|
|
||||||
let relationshipService: RelationshipDataService;
|
let relationshipService: RelationshipDataService;
|
||||||
let dsoNameService: DSONameService;
|
let dsoNameService: DSONameService;
|
||||||
|
let vocabularyServiceStub: any;
|
||||||
|
let itemService: ItemDataService;
|
||||||
|
|
||||||
let editMetadataValue: DsoEditMetadataValue;
|
let editMetadataValue: DsoEditMetadataValue;
|
||||||
let metadataValue: MetadataValue;
|
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 {
|
function initServices(): void {
|
||||||
relationshipService = jasmine.createSpyObj('relationshipService', {
|
relationshipService = jasmine.createSpyObj('relationshipService', {
|
||||||
@@ -35,6 +114,10 @@ describe('DsoEditMetadataValueComponent', () => {
|
|||||||
dsoNameService = jasmine.createSpyObj('dsoNameService', {
|
dsoNameService = jasmine.createSpyObj('dsoNameService', {
|
||||||
getName: 'Related Name',
|
getName: 'Related Name',
|
||||||
});
|
});
|
||||||
|
itemService = jasmine.createSpyObj('itemService', {
|
||||||
|
findByHref: createSuccessfulRemoteDataObject$(item)
|
||||||
|
});
|
||||||
|
vocabularyServiceStub = new VocabularyServiceStub();
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
@@ -45,6 +128,11 @@ describe('DsoEditMetadataValueComponent', () => {
|
|||||||
authority: undefined,
|
authority: undefined,
|
||||||
});
|
});
|
||||||
editMetadataValue = new DsoEditMetadataValue(metadataValue);
|
editMetadataValue = new DsoEditMetadataValue(metadataValue);
|
||||||
|
dso = Object.assign(new DSpaceObject(), {
|
||||||
|
_links: {
|
||||||
|
self: { href: 'fake-dso-url/dso' }
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
initServices();
|
initServices();
|
||||||
|
|
||||||
@@ -54,6 +142,8 @@ describe('DsoEditMetadataValueComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: RelationshipDataService, useValue: relationshipService },
|
{ provide: RelationshipDataService, useValue: relationshipService },
|
||||||
{ provide: DSONameService, useValue: dsoNameService },
|
{ provide: DSONameService, useValue: dsoNameService },
|
||||||
|
{ provide: VocabularyService, useValue: vocabularyServiceStub },
|
||||||
|
{ provide: ItemDataService, useValue: itemService }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -63,6 +153,7 @@ describe('DsoEditMetadataValueComponent', () => {
|
|||||||
fixture = TestBed.createComponent(DsoEditMetadataValueComponent);
|
fixture = TestBed.createComponent(DsoEditMetadataValueComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.mdValue = editMetadataValue;
|
component.mdValue = editMetadataValue;
|
||||||
|
component.dso = dso;
|
||||||
component.saving$ = of(false);
|
component.saving$ = of(false);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -144,6 +235,222 @@ describe('DsoEditMetadataValueComponent', () => {
|
|||||||
assertButton(DRAG_BTN, true, false);
|
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 {
|
function assertButton(name: string, exists: boolean, disabled: boolean = false): void {
|
||||||
describe(`${name} button`, () => {
|
describe(`${name} button`, () => {
|
||||||
let btn: DebugElement;
|
let btn: DebugElement;
|
||||||
|
@@ -8,10 +8,25 @@ import {
|
|||||||
import { RelationshipDataService } from '../../../core/data/relationship-data.service';
|
import { RelationshipDataService } from '../../../core/data/relationship-data.service';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.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 { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
|
||||||
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
||||||
import { EMPTY } from 'rxjs/internal/observable/empty';
|
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({
|
@Component({
|
||||||
selector: 'ds-dso-edit-metadata-value',
|
selector: 'ds-dso-edit-metadata-value',
|
||||||
@@ -51,6 +66,11 @@ export class DsoEditMetadataValueComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() isOnlyValue = false;
|
@Input() isOnlyValue = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetadataField to edit
|
||||||
|
*/
|
||||||
|
@Input() mdField?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits when the user clicked edit
|
* Emits when the user clicked edit
|
||||||
*/
|
*/
|
||||||
@@ -82,6 +102,12 @@ export class DsoEditMetadataValueComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public DsoEditMetadataChangeTypeEnum = DsoEditMetadataChangeType;
|
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)
|
* 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<string | null>;
|
mdRepresentationName$: Observable<string | null>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<DynamicOneboxModel | DynamicScrollableDropdownModel>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable with information about the authority vocabulary used
|
||||||
|
*/
|
||||||
|
private vocabulary$: Observable<Vocabulary>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observables with information about the authority vocabulary type used
|
||||||
|
*/
|
||||||
|
private isAuthorityControlled$: Observable<boolean>;
|
||||||
|
private isHierarchicalVocabulary$: Observable<boolean>;
|
||||||
|
private isScrollableVocabulary$: Observable<boolean>;
|
||||||
|
private isSuggesterVocabulary$: Observable<boolean>;
|
||||||
|
|
||||||
constructor(protected relationshipService: RelationshipDataService,
|
constructor(protected relationshipService: RelationshipDataService,
|
||||||
protected dsoNameService: DSONameService) {
|
protected dsoNameService: DSONameService,
|
||||||
|
protected vocabularyService: VocabularyService,
|
||||||
|
protected itemService: ItemDataService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initVirtualProperties();
|
this.initVirtualProperties();
|
||||||
|
this.initAuthorityProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,4 +181,173 @@ export class DsoEditMetadataValueComponent implements OnInit {
|
|||||||
map((mdRepresentation: ItemMetadataRepresentation) => mdRepresentation ? this.dsoNameService.getName(mdRepresentation) : null),
|
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<Collection> = 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<boolean> {
|
||||||
|
return this.isAuthorityControlled$;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if configured vocabulary is Hierarchical or not
|
||||||
|
*/
|
||||||
|
isHierarchicalVocabulary(): Observable<boolean> {
|
||||||
|
return this.isHierarchicalVocabulary$;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if configured vocabulary is Scrollable or not
|
||||||
|
*/
|
||||||
|
isScrollableVocabulary(): Observable<boolean> {
|
||||||
|
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<boolean> {
|
||||||
|
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<DynamicOneboxModel | DynamicScrollableDropdownModel> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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 { 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 { 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 { ThemedDsoEditMetadataComponent } from './dso-edit-metadata/themed-dso-edit-metadata.component';
|
||||||
|
import { FormModule } from '../shared/form/form.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
FormModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
DsoEditMetadataComponent,
|
DsoEditMetadataComponent,
|
||||||
|
@@ -2152,6 +2152,8 @@
|
|||||||
|
|
||||||
"item.edit.metadata.edit.value": "Edit value",
|
"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.confirm": "Confirm",
|
||||||
|
|
||||||
"item.edit.metadata.edit.buttons.drag": "Drag to reorder",
|
"item.edit.metadata.edit.buttons.drag": "Drag to reorder",
|
||||||
@@ -2206,6 +2208,12 @@
|
|||||||
|
|
||||||
"item.edit.metadata.save-button": "Save",
|
"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.field": "Field",
|
||||||
|
|
||||||
"item.edit.modify.overview.language": "Language",
|
"item.edit.modify.overview.language": "Language",
|
||||||
|
Reference in New Issue
Block a user