diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html
index cbb587cca48..4d3b948a583 100644
--- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html
+++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html
@@ -12,7 +12,7 @@
{{bitstreamRD?.payload?.name}} ({{bitstreamRD?.payl
- div > label {
+ margin-top: 1.75rem;
+}
+
diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts
index 67f8866e6df..44e48182fd9 100644
--- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts
+++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts
@@ -22,6 +22,8 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } f
import { getEntityEditRoute } from '../../item-page/item-page-routing-paths';
import { createPaginatedList } from '../../shared/testing/utils.test';
import { Item } from '../../core/shared/item.model';
+import { MetadataValueFilter } from '../../core/shared/metadata.models';
+import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
@@ -31,24 +33,27 @@ let notificationsService: NotificationsService;
let formService: DynamicFormService;
let bitstreamService: BitstreamDataService;
let bitstreamFormatService: BitstreamFormatDataService;
+let dsoNameService: DSONameService;
let bitstream: Bitstream;
let selectedFormat: BitstreamFormat;
let allFormats: BitstreamFormat[];
let router: Router;
+let comp: EditBitstreamPageComponent;
+let fixture: ComponentFixture;
+
describe('EditBitstreamPageComponent', () => {
- let comp: EditBitstreamPageComponent;
- let fixture: ComponentFixture;
- beforeEach(waitForAsync(() => {
+ beforeEach(() => {
allFormats = [
Object.assign({
id: '1',
shortDescription: 'Unknown',
description: 'Unknown format',
supportLevel: BitstreamFormatSupportLevel.Unknown,
+ mimetype: 'application/octet-stream',
_links: {
- self: { href: 'format-selflink-1' }
+ self: {href: 'format-selflink-1'}
}
}),
Object.assign({
@@ -56,8 +61,9 @@ describe('EditBitstreamPageComponent', () => {
shortDescription: 'PNG',
description: 'Portable Network Graphics',
supportLevel: BitstreamFormatSupportLevel.Known,
+ mimetype: 'image/png',
_links: {
- self: { href: 'format-selflink-2' }
+ self: {href: 'format-selflink-2'}
}
}),
Object.assign({
@@ -65,19 +71,14 @@ describe('EditBitstreamPageComponent', () => {
shortDescription: 'GIF',
description: 'Graphics Interchange Format',
supportLevel: BitstreamFormatSupportLevel.Known,
+ mimetype: 'image/gif',
_links: {
- self: { href: 'format-selflink-3' }
+ self: {href: 'format-selflink-3'}
}
})
] as BitstreamFormat[];
selectedFormat = allFormats[1];
- notificationsService = jasmine.createSpyObj('notificationsService',
- {
- info: infoNotification,
- warning: warningNotification,
- success: successNotification
- }
- );
+
formService = Object.assign({
createFormGroup: (fModel: DynamicFormControlModel[]) => {
const controls = {};
@@ -90,156 +91,418 @@ describe('EditBitstreamPageComponent', () => {
return undefined;
}
});
- bitstream = Object.assign(new Bitstream(), {
- metadata: {
- 'dc.description': [
- {
- value: 'Bitstream description'
- }
- ],
- 'dc.title': [
- {
- value: 'Bitstream title'
- }
- ]
- },
- format: createSuccessfulRemoteDataObject$(selectedFormat),
- _links: {
- self: 'bitstream-selflink'
- },
- bundle: createSuccessfulRemoteDataObject$({
- item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
- uuid: 'some-uuid'
- }))
- })
- });
- bitstreamService = jasmine.createSpyObj('bitstreamService', {
- findById: createSuccessfulRemoteDataObject$(bitstream),
- update: createSuccessfulRemoteDataObject$(bitstream),
- updateFormat: createSuccessfulRemoteDataObject$(bitstream),
- commitUpdates: {},
- patch: {}
- });
+
bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', {
findAll: createSuccessfulRemoteDataObject$(createPaginatedList(allFormats))
});
- TestBed.configureTestingModule({
- imports: [TranslateModule.forRoot(), RouterTestingModule],
- declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective],
- providers: [
- { provide: NotificationsService, useValue: notificationsService },
- { provide: DynamicFormService, useValue: formService },
- { provide: ActivatedRoute, useValue: { data: observableOf({ bitstream: createSuccessfulRemoteDataObject(bitstream) }), snapshot: { queryParams: {} } } },
- { provide: BitstreamDataService, useValue: bitstreamService },
- { provide: BitstreamFormatDataService, useValue: bitstreamFormatService },
- ChangeDetectorRef
- ],
- schemas: [NO_ERRORS_SCHEMA]
- }).compileComponents();
-
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(EditBitstreamPageComponent);
- comp = fixture.componentInstance;
- fixture.detectChanges();
- router = TestBed.inject(Router);
- spyOn(router, 'navigate');
+ notificationsService = jasmine.createSpyObj('notificationsService',
+ {
+ info: infoNotification,
+ warning: warningNotification,
+ success: successNotification
+ }
+ );
});
- describe('on startup', () => {
- let rawForm;
+ describe('EditBitstreamPageComponent no IIIF fields', () => {
- beforeEach(() => {
- rawForm = comp.formGroup.getRawValue();
- });
+ beforeEach(waitForAsync(() => {
- it('should fill in the bitstream\'s title', () => {
- expect(rawForm.fileNamePrimaryContainer.fileName).toEqual(bitstream.name);
- });
+ const bundleName = 'ORIGINAL';
- it('should fill in the bitstream\'s description', () => {
- expect(rawForm.descriptionContainer.description).toEqual(bitstream.firstMetadataValue('dc.description'));
- });
+ bitstream = Object.assign(new Bitstream(), {
+ metadata: {
+ 'dc.description': [
+ {
+ value: 'Bitstream description'
+ }
+ ],
+ 'dc.title': [
+ {
+ value: 'Bitstream title'
+ }
+ ]
+ },
+ format: createSuccessfulRemoteDataObject$(selectedFormat),
+ _links: {
+ self: 'bitstream-selflink'
+ },
+ bundle: createSuccessfulRemoteDataObject$({
+ item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
+ uuid: 'some-uuid',
+ firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
+ return undefined;
+ },
+ }))
+ })
+ });
+ bitstreamService = jasmine.createSpyObj('bitstreamService', {
+ findById: createSuccessfulRemoteDataObject$(bitstream),
+ update: createSuccessfulRemoteDataObject$(bitstream),
+ updateFormat: createSuccessfulRemoteDataObject$(bitstream),
+ commitUpdates: {},
+ patch: {}
+ });
+ bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', {
+ findAll: createSuccessfulRemoteDataObject$(createPaginatedList(allFormats))
+ });
+ dsoNameService = jasmine.createSpyObj('dsoNameService', {
+ getName: bundleName
+ });
- it('should select the correct format', () => {
- expect(rawForm.formatContainer.selectedFormat).toEqual(selectedFormat.id);
- });
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), RouterTestingModule],
+ declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective],
+ providers: [
+ {provide: NotificationsService, useValue: notificationsService},
+ {provide: DynamicFormService, useValue: formService},
+ {provide: ActivatedRoute,
+ useValue: {
+ data: observableOf({bitstream: createSuccessfulRemoteDataObject(bitstream)}),
+ snapshot: {queryParams: {}}
+ }
+ },
+ {provide: BitstreamDataService, useValue: bitstreamService},
+ {provide: DSONameService, useValue: dsoNameService},
+ {provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
+ ChangeDetectorRef
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
- it('should put the \"New Format\" input on invisible', () => {
- expect(comp.formLayout.newFormat.grid.host).toContain('invisible');
- });
- });
+ }));
- describe('when an unknown format is selected', () => {
beforeEach(() => {
- comp.updateNewFormatLayout(allFormats[0].id);
+ fixture = TestBed.createComponent(EditBitstreamPageComponent);
+ comp = fixture.componentInstance;
+ fixture.detectChanges();
+ router = TestBed.inject(Router);
+ spyOn(router, 'navigate');
});
- it('should remove the invisible class from the \"New Format\" input', () => {
- expect(comp.formLayout.newFormat.grid.host).not.toContain('invisible');
- });
- });
+ describe('on startup', () => {
+ let rawForm;
- describe('onSubmit', () => {
- describe('when selected format hasn\'t changed', () => {
beforeEach(() => {
- comp.onSubmit();
+ rawForm = comp.formGroup.getRawValue();
+ });
+
+ it('should fill in the bitstream\'s title', () => {
+ expect(rawForm.fileNamePrimaryContainer.fileName).toEqual(bitstream.name);
});
- it('should call update', () => {
- expect(bitstreamService.update).toHaveBeenCalled();
+ it('should fill in the bitstream\'s description', () => {
+ expect(rawForm.descriptionContainer.description).toEqual(bitstream.firstMetadataValue('dc.description'));
});
- it('should commit the updates', () => {
- expect(bitstreamService.commitUpdates).toHaveBeenCalled();
+ it('should select the correct format', () => {
+ expect(rawForm.formatContainer.selectedFormat).toEqual(selectedFormat.id);
+ });
+
+ it('should put the \"New Format\" input on invisible', () => {
+ expect(comp.formLayout.newFormat.grid.host).toContain('invisible');
});
});
- describe('when selected format has changed', () => {
+ describe('when an unknown format is selected', () => {
beforeEach(() => {
- comp.formGroup.patchValue({
- formatContainer: {
- selectedFormat: allFormats[2].id
- }
- });
- fixture.detectChanges();
- comp.onSubmit();
+ comp.updateNewFormatLayout(allFormats[0].id);
});
- it('should call update', () => {
- expect(bitstreamService.update).toHaveBeenCalled();
+ it('should remove the invisible class from the \"New Format\" input', () => {
+ expect(comp.formLayout.newFormat.grid.host).not.toContain('invisible');
});
+ });
+
+ describe('onSubmit', () => {
+ describe('when selected format hasn\'t changed', () => {
+ beforeEach(() => {
+ comp.onSubmit();
+ });
- it('should call updateFormat', () => {
- expect(bitstreamService.updateFormat).toHaveBeenCalled();
+ it('should call update', () => {
+ expect(bitstreamService.update).toHaveBeenCalled();
+ });
+
+ it('should commit the updates', () => {
+ expect(bitstreamService.commitUpdates).toHaveBeenCalled();
+ });
});
- it('should commit the updates', () => {
- expect(bitstreamService.commitUpdates).toHaveBeenCalled();
+ describe('when selected format has changed', () => {
+ beforeEach(() => {
+ comp.formGroup.patchValue({
+ formatContainer: {
+ selectedFormat: allFormats[2].id
+ }
+ });
+ fixture.detectChanges();
+ comp.onSubmit();
+ });
+
+ it('should call update', () => {
+ expect(bitstreamService.update).toHaveBeenCalled();
+ });
+
+ it('should call updateFormat', () => {
+ expect(bitstreamService.updateFormat).toHaveBeenCalled();
+ });
+
+ it('should commit the updates', () => {
+ expect(bitstreamService.commitUpdates).toHaveBeenCalled();
+ });
});
});
- });
- describe('when the cancel button is clicked', () => {
- it('should call navigateToItemEditBitstreams method', () => {
- spyOn(comp, 'navigateToItemEditBitstreams');
- comp.onCancel();
- expect(comp.navigateToItemEditBitstreams).toHaveBeenCalled();
+ describe('when the cancel button is clicked', () => {
+ it('should call navigateToItemEditBitstreams method', () => {
+ spyOn(comp, 'navigateToItemEditBitstreams');
+ comp.onCancel();
+ expect(comp.navigateToItemEditBitstreams).toHaveBeenCalled();
+ });
});
- });
- describe('when navigateToItemEditBitstreams is called, and the component has an itemId', () => {
- it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => {
- comp.itemId = 'some-uuid1';
- comp.navigateToItemEditBitstreams();
- expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid1'), 'bitstreams']);
+ describe('when navigateToItemEditBitstreams is called, and the component has an itemId', () => {
+ it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => {
+ comp.itemId = 'some-uuid1';
+ comp.navigateToItemEditBitstreams();
+ expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid1'), 'bitstreams']);
+ });
+ });
+ describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => {
+ it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => {
+ comp.itemId = undefined;
+ comp.navigateToItemEditBitstreams();
+ expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid'), 'bitstreams']);
+ });
});
});
- describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => {
- it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => {
- comp.itemId = undefined;
- comp.navigateToItemEditBitstreams();
- expect(router.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid'), 'bitstreams']);
+
+ describe('EditBitstreamPageComponent with IIIF fields', () => {
+
+ const bundleName = 'ORIGINAL';
+
+ beforeEach(waitForAsync(() => {
+
+ bitstream = Object.assign(new Bitstream(), {
+ metadata: {
+ 'dc.description': [
+ {
+ value: 'Bitstream description'
+ }
+ ],
+ 'dc.title': [
+ {
+ value: 'Bitstream title'
+ }
+ ],
+ 'iiif.label': [
+ {
+ value: 'chapter one'
+ }
+ ],
+ 'iiif.toc': [
+ {
+ value: 'chapter one'
+ }
+ ],
+ 'iiif.image.width': [
+ {
+ value: '2400'
+ }
+ ],
+ 'iiif.image.height': [
+ {
+ value: '2800'
+ }
+ ],
+ },
+ format: createSuccessfulRemoteDataObject$(allFormats[1]),
+ _links: {
+ self: 'bitstream-selflink'
+ },
+ bundle: createSuccessfulRemoteDataObject$({
+ item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
+ uuid: 'some-uuid',
+ firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
+ return 'True';
+ }
+ }))
+ })
+ });
+ bitstreamService = jasmine.createSpyObj('bitstreamService', {
+ findById: createSuccessfulRemoteDataObject$(bitstream),
+ update: createSuccessfulRemoteDataObject$(bitstream),
+ updateFormat: createSuccessfulRemoteDataObject$(bitstream),
+ commitUpdates: {},
+ patch: {}
+ });
+
+ dsoNameService = jasmine.createSpyObj('dsoNameService', {
+ getName: bundleName
+ });
+
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), RouterTestingModule],
+ declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective],
+ providers: [
+ {provide: NotificationsService, useValue: notificationsService},
+ {provide: DynamicFormService, useValue: formService},
+ {
+ provide: ActivatedRoute,
+ useValue: {
+ data: observableOf({bitstream: createSuccessfulRemoteDataObject(bitstream)}),
+ snapshot: {queryParams: {}}
+ }
+ },
+ {provide: BitstreamDataService, useValue: bitstreamService},
+ {provide: DSONameService, useValue: dsoNameService},
+ {provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
+ ChangeDetectorRef
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EditBitstreamPageComponent);
+ comp = fixture.componentInstance;
+ fixture.detectChanges();
+ router = TestBed.inject(Router);
+ spyOn(router, 'navigate');
});
+
+
+ describe('on startup', () => {
+ let rawForm;
+
+ beforeEach(() => {
+ rawForm = comp.formGroup.getRawValue();
+ });
+ it('should set isIIIF to true', () => {
+ expect(comp.isIIIF).toBeTrue();
+ });
+ it('should fill in the iiif label', () => {
+ expect(rawForm.iiifLabelContainer.iiifLabel).toEqual('chapter one');
+ });
+ it('should fill in the iiif toc', () => {
+ expect(rawForm.iiifTocContainer.iiifToc).toEqual('chapter one');
+ });
+ it('should fill in the iiif width', () => {
+ expect(rawForm.iiifWidthContainer.iiifWidth).toEqual('2400');
+ });
+ it('should fill in the iiif height', () => {
+ expect(rawForm.iiifHeightContainer.iiifHeight).toEqual('2800');
+ });
+ });
+ });
+
+ describe('ignore OTHERCONTENT bundle', () => {
+
+ const bundleName = 'OTHERCONTENT';
+
+ beforeEach(waitForAsync(() => {
+
+ bitstream = Object.assign(new Bitstream(), {
+ metadata: {
+ 'dc.description': [
+ {
+ value: 'Bitstream description'
+ }
+ ],
+ 'dc.title': [
+ {
+ value: 'Bitstream title'
+ }
+ ],
+ 'iiif.label': [
+ {
+ value: 'chapter one'
+ }
+ ],
+ 'iiif.toc': [
+ {
+ value: 'chapter one'
+ }
+ ],
+ 'iiif.image.width': [
+ {
+ value: '2400'
+ }
+ ],
+ 'iiif.image.height': [
+ {
+ value: '2800'
+ }
+ ],
+ },
+ format: createSuccessfulRemoteDataObject$(allFormats[2]),
+ _links: {
+ self: 'bitstream-selflink'
+ },
+ bundle: createSuccessfulRemoteDataObject$({
+ item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
+ uuid: 'some-uuid',
+ firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
+ return 'True';
+ }
+ }))
+ })
+ });
+ bitstreamService = jasmine.createSpyObj('bitstreamService', {
+ findById: createSuccessfulRemoteDataObject$(bitstream),
+ update: createSuccessfulRemoteDataObject$(bitstream),
+ updateFormat: createSuccessfulRemoteDataObject$(bitstream),
+ commitUpdates: {},
+ patch: {}
+ });
+
+ dsoNameService = jasmine.createSpyObj('dsoNameService', {
+ getName: bundleName
+ });
+
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), RouterTestingModule],
+ declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective],
+ providers: [
+ {provide: NotificationsService, useValue: notificationsService},
+ {provide: DynamicFormService, useValue: formService},
+ {provide: ActivatedRoute,
+ useValue: {
+ data: observableOf({bitstream: createSuccessfulRemoteDataObject(bitstream)}),
+ snapshot: {queryParams: {}}
+ }
+ },
+ {provide: BitstreamDataService, useValue: bitstreamService},
+ {provide: DSONameService, useValue: dsoNameService},
+ {provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
+ ChangeDetectorRef
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EditBitstreamPageComponent);
+ comp = fixture.componentInstance;
+ fixture.detectChanges();
+ router = TestBed.inject(Router);
+ spyOn(router, 'navigate');
+ });
+
+ describe('EditBitstreamPageComponent with IIIF fields', () => {
+ let rawForm;
+
+ beforeEach(() => {
+ rawForm = comp.formGroup.getRawValue();
+ });
+
+ it('should NOT set isIIIF to true', () => {
+ expect(comp.isIIIF).toBeFalse();
+ });
+ it('should put the \"IIIF Label\" input not to be shown', () => {
+ expect(rawForm.iiifLabelContainer).toBeFalsy();
+ });
+ });
});
+
});
diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts
index f6ece7f4fae..9a59df4b95a 100644
--- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts
+++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts
@@ -1,16 +1,27 @@
-import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ OnDestroy,
+ OnInit
+} from '@angular/core';
import { Bitstream } from '../../core/shared/bitstream.model';
import { ActivatedRoute, Router } from '@angular/router';
import { map, mergeMap, switchMap } from 'rxjs/operators';
-import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
+import {
+ combineLatest,
+ combineLatest as observableCombineLatest,
+ Observable,
+ of as observableOf,
+ Subscription
+} from 'rxjs';
import {
DynamicFormControlModel,
DynamicFormGroupModel,
DynamicFormLayout,
DynamicFormService,
DynamicInputModel,
- DynamicSelectModel,
- DynamicTextAreaModel
+ DynamicSelectModel
} from '@ng-dynamic-forms/core';
import { FormGroup } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
@@ -28,13 +39,19 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { BitstreamFormatDataService } from '../../core/data/bitstream-format-data.service';
import { BitstreamFormat } from '../../core/shared/bitstream-format.model';
import { BitstreamFormatSupportLevel } from '../../core/shared/bitstream-format-support-level';
-import { hasValue, isNotEmpty } from '../../shared/empty.util';
+import { hasValue, isNotEmpty, isEmpty } from '../../shared/empty.util';
import { Metadata } from '../../core/shared/metadata.utils';
import { Location } from '@angular/common';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { getEntityEditRoute, getItemEditRoute } from '../../item-page/item-page-routing-paths';
import { Bundle } from '../../core/shared/bundle.model';
+import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
+import { Item } from '../../core/shared/item.model';
+import {
+ DsDynamicInputModel
+} from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model';
+import { DsDynamicTextAreaModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-textarea.model';
@Component({
selector: 'ds-edit-bitstream-page',
@@ -94,6 +111,26 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/
NOTIFICATIONS_PREFIX = 'bitstream.edit.notifications.';
+ /**
+ * IIIF image width metadata key
+ */
+ IMAGE_WIDTH_METADATA = 'iiif.image.width';
+
+ /**
+ * IIIF image height metadata key
+ */
+ IMAGE_HEIGHT_METADATA = 'iiif.image.height';
+
+ /**
+ * IIIF table of contents metadata key
+ */
+ IIIF_TOC_METADATA = 'iiif.toc';
+
+ /**
+ * IIIF label metadata key
+ */
+ IIIF_LABEL_METADATA = 'iiif.label';
+
/**
* Options for fetching all bitstream formats
*/
@@ -102,7 +139,8 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/**
* The Dynamic Input Model for the file's name
*/
- fileNameModel = new DynamicInputModel({
+ fileNameModel = new DsDynamicInputModel({
+ hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '',
id: 'fileName',
name: 'fileName',
required: true,
@@ -118,14 +156,16 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
* The Dynamic Switch Model for the file's name
*/
primaryBitstreamModel = new DynamicCustomSwitchModel({
- id: 'primaryBitstream',
- name: 'primaryBitstream'
- });
+ id: 'primaryBitstream',
+ name: 'primaryBitstream'
+ }
+ );
/**
* The Dynamic TextArea Model for the file's description
*/
- descriptionModel = new DynamicTextAreaModel({
+ descriptionModel = new DsDynamicTextAreaModel({
+ hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '',
id: 'description',
name: 'description',
rows: 10
@@ -147,10 +187,87 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
name: 'newFormat'
});
+ /**
+ * The Dynamic Input Model for the iiif label
+ */
+ iiifLabelModel = new DsDynamicInputModel({
+ hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '',
+ id: 'iiifLabel',
+ name: 'iiifLabel'
+ },
+ {
+ grid: {
+ host: 'col col-lg-6 d-inline-block'
+ }
+ });
+ iiifLabelContainer = new DynamicFormGroupModel({
+ id: 'iiifLabelContainer',
+ group: [this.iiifLabelModel]
+ },{
+ grid: {
+ host: 'form-row'
+ }
+ });
+
+ iiifTocModel = new DsDynamicInputModel({
+ hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '',
+ id: 'iiifToc',
+ name: 'iiifToc',
+ },{
+ grid: {
+ host: 'col col-lg-6 d-inline-block'
+ }
+ });
+ iiifTocContainer = new DynamicFormGroupModel({
+ id: 'iiifTocContainer',
+ group: [this.iiifTocModel]
+ },{
+ grid: {
+ host: 'form-row'
+ }
+ });
+
+ iiifWidthModel = new DsDynamicInputModel({
+ hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '',
+ id: 'iiifWidth',
+ name: 'iiifWidth',
+ },{
+ grid: {
+ host: 'col col-lg-6 d-inline-block'
+ }
+ });
+ iiifWidthContainer = new DynamicFormGroupModel({
+ id: 'iiifWidthContainer',
+ group: [this.iiifWidthModel]
+ },{
+ grid: {
+ host: 'form-row'
+ }
+ });
+
+ iiifHeightModel = new DsDynamicInputModel({
+ hasSelectableMetadata: false, metadataFields: [], repeatable: false, submissionId: '',
+ id: 'iiifHeight',
+ name: 'iiifHeight'
+ },{
+ grid: {
+ host: 'col col-lg-6 d-inline-block'
+ }
+ });
+ iiifHeightContainer = new DynamicFormGroupModel({
+ id: 'iiifHeightContainer',
+ group: [this.iiifHeightModel]
+ },{
+ grid: {
+ host: 'form-row'
+ }
+ });
+
/**
* All input models in a simple array for easier iterations
*/
- inputModels = [this.fileNameModel, this.primaryBitstreamModel, this.descriptionModel, this.selectedFormatModel, this.newFormatModel];
+ inputModels = [this.fileNameModel, this.primaryBitstreamModel, this.descriptionModel, this.selectedFormatModel,
+ this.newFormatModel];
/**
* The dynamic form fields used for editing the information of a bitstream
@@ -163,7 +280,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
this.fileNameModel,
this.primaryBitstreamModel
]
- }),
+ },{
+ grid: {
+ host: 'form-row'
+ }
+ }),
new DynamicFormGroupModel({
id: 'descriptionContainer',
group: [
@@ -254,18 +375,27 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/
entityType: string;
+ /**
+ * Set to true when the parent item supports IIIF.
+ */
+ isIIIF = false;
+
+
/**
* Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array}
*/
protected subs: Subscription[] = [];
+
constructor(private route: ActivatedRoute,
private router: Router,
+ private changeDetectorRef: ChangeDetectorRef,
private location: Location,
private formService: DynamicFormService,
private translate: TranslateService,
private bitstreamService: BitstreamDataService,
+ private dsoNameService: DSONameService,
private notificationsService: NotificationsService,
private bitstreamFormatService: BitstreamFormatDataService) {
}
@@ -277,7 +407,6 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
* - Translate the form labels and hints
*/
ngOnInit(): void {
- this.formGroup = this.formService.createFormGroup(this.formModel);
this.itemId = this.route.snapshot.queryParams.itemId;
this.entityType = this.route.snapshot.queryParams.entityType;
@@ -301,13 +430,10 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
).subscribe(([bitstream, allFormats]) => {
this.bitstream = bitstream as Bitstream;
this.formats = allFormats.page;
- this.updateFormatModel();
- this.updateForm(this.bitstream);
+ this.setIiifStatus(this.bitstream);
})
);
- this.updateFieldTranslations();
-
this.subs.push(
this.translate.onLangChange
.subscribe(() => {
@@ -316,6 +442,16 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
);
}
+ /**
+ * Initializes the form.
+ */
+ setForm() {
+ this.formGroup = this.formService.createFormGroup(this.formModel);
+ this.updateFormatModel();
+ this.updateForm(this.bitstream);
+ this.updateFieldTranslations();
+ }
+
/**
* Update the current form values with bitstream properties
* @param bitstream
@@ -333,6 +469,22 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
newFormat: hasValue(bitstream.firstMetadata('dc.format')) ? bitstream.firstMetadata('dc.format').value : undefined
}
});
+ if (this.isIIIF) {
+ this.formGroup.patchValue({
+ iiifLabelContainer: {
+ iiifLabel: bitstream.firstMetadataValue(this.IIIF_LABEL_METADATA)
+ },
+ iiifTocContainer: {
+ iiifToc: bitstream.firstMetadataValue(this.IIIF_TOC_METADATA)
+ },
+ iiifWidthContainer: {
+ iiifWidth: bitstream.firstMetadataValue(this.IMAGE_WIDTH_METADATA)
+ },
+ iiifHeightContainer: {
+ iiifHeight: bitstream.firstMetadataValue(this.IMAGE_HEIGHT_METADATA)
+ }
+ });
+ }
this.bitstream.format.pipe(
getAllSucceededRemoteDataPayload()
).subscribe((format: BitstreamFormat) => {
@@ -467,6 +619,32 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
const primary = rawForm.fileNamePrimaryContainer.primaryBitstream;
Metadata.setFirstValue(newMetadata, 'dc.title', rawForm.fileNamePrimaryContainer.fileName);
Metadata.setFirstValue(newMetadata, 'dc.description', rawForm.descriptionContainer.description);
+ if (this.isIIIF) {
+ // It's helpful to remove these metadata elements entirely when the form value is empty.
+ // This avoids potential issues on the REST side and makes it possible to do things like
+ // remove an existing "table of contents" entry.
+ if (isEmpty(rawForm.iiifLabelContainer.iiifLabel)) {
+
+ delete newMetadata[this.IIIF_LABEL_METADATA];
+ } else {
+ Metadata.setFirstValue(newMetadata, this.IIIF_LABEL_METADATA, rawForm.iiifLabelContainer.iiifLabel);
+ }
+ if (isEmpty(rawForm.iiifTocContainer.iiifToc)) {
+ delete newMetadata[this.IIIF_TOC_METADATA];
+ } else {
+ Metadata.setFirstValue(newMetadata, this.IIIF_TOC_METADATA, rawForm.iiifTocContainer.iiifToc);
+ }
+ if (isEmpty(rawForm.iiifWidthContainer.iiifWidth)) {
+ delete newMetadata[this.IMAGE_WIDTH_METADATA];
+ } else {
+ Metadata.setFirstValue(newMetadata, this.IMAGE_WIDTH_METADATA, rawForm.iiifWidthContainer.iiifWidth);
+ }
+ if (isEmpty(rawForm.iiifHeightContainer.iiifHeight)) {
+ delete newMetadata[this.IMAGE_HEIGHT_METADATA];
+ } else {
+ Metadata.setFirstValue(newMetadata, this.IMAGE_HEIGHT_METADATA, rawForm.iiifHeightContainer.iiifHeight);
+ }
+ }
if (isNotEmpty(rawForm.formatContainer.newFormat)) {
Metadata.setFirstValue(newMetadata, 'dc.format', rawForm.formatContainer.newFormat);
}
@@ -497,6 +675,58 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
}
}
+ /**
+ * Verifies that the parent item is iiif-enabled. Checks bitstream mimetype to be
+ * sure it's an image, excluding bitstreams in the THUMBNAIL or OTHERCONTENT bundles.
+ * @param bitstream
+ */
+ setIiifStatus(bitstream: Bitstream) {
+
+ const regexExcludeBundles = /OTHERCONTENT|THUMBNAIL|LICENSE/;
+ const regexIIIFItem = /true|yes/i;
+
+ const isImage$ = this.bitstream.format.pipe(
+ getFirstSucceededRemoteData(),
+ map((format: RemoteData) => format.payload.mimetype.includes('image/')));
+
+ const isIIIFBundle$ = this.bitstream.bundle.pipe(
+ getFirstSucceededRemoteData(),
+ map((bundle: RemoteData) =>
+ this.dsoNameService.getName(bundle.payload).match(regexExcludeBundles) == null));
+
+ const isEnabled$ = this.bitstream.bundle.pipe(
+ getFirstSucceededRemoteData(),
+ map((bundle: RemoteData) => bundle.payload.item.pipe(
+ getFirstSucceededRemoteData(),
+ map((item: RemoteData- ) =>
+ (item.payload.firstMetadataValue('dspace.iiif.enabled') &&
+ item.payload.firstMetadataValue('dspace.iiif.enabled').match(regexIIIFItem) !== null)
+ ))));
+
+ const iiifSub = combineLatest(
+ isImage$,
+ isIIIFBundle$,
+ isEnabled$
+ ).subscribe(([isImage, isIIIFBundle, isEnabled]) => {
+ if (isImage && isIIIFBundle && isEnabled) {
+ this.isIIIF = true;
+ this.inputModels.push(this.iiifLabelModel);
+ this.formModel.push(this.iiifLabelContainer);
+ this.inputModels.push(this.iiifTocModel);
+ this.formModel.push(this.iiifTocContainer);
+ this.inputModels.push(this.iiifWidthModel);
+ this.formModel.push(this.iiifWidthContainer);
+ this.inputModels.push(this.iiifHeightModel);
+ this.formModel.push(this.iiifHeightContainer);
+ }
+ this.setForm();
+ this.changeDetectorRef.detectChanges();
+ });
+
+ this.subs.push(iiifSub);
+
+ }
+
/**
* Unsubscribe from open subscriptions
*/
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5
index 7753532838f..f742273edbe 100644
--- a/src/assets/i18n/en.json5
+++ b/src/assets/i18n/en.json5
@@ -587,6 +587,23 @@
"bitstream.edit.notifications.error.format.title": "An error occurred saving the bitstream's format",
+ "bitstream.edit.form.iiifLabel.label": "IIIF Label",
+
+ "bitstream.edit.form.iiifLabel.hint": "Canvas label for this image. If not provided default label will be used.",
+
+ "bitstream.edit.form.iiifToc.label": "IIIF Table of Contents",
+
+ "bitstream.edit.form.iiifToc.hint": "Adding text here makes this the start of a new table of contents range.",
+
+ "bitstream.edit.form.iiifWidth.label": "IIIF Canvas Width",
+
+ "bitstream.edit.form.iiifWidth.hint": "The canvas width should usually match the image width.",
+
+ "bitstream.edit.form.iiifHeight.label": "IIIF Canvas Height",
+
+ "bitstream.edit.form.iiifHeight.hint": "The canvas height should usually match the image height.",
+
+
"bitstream.edit.notifications.saved.content": "Your changes to this bitstream were saved.",
"bitstream.edit.notifications.saved.title": "Bitstream saved",