mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #1417 from 4Science/CST-4878
Upload files - Edit bitstream (improvements and bug fixes)
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
<div>
|
||||
<ds-form *ngIf="formModel"
|
||||
#formRef="formComponent"
|
||||
[formId]="formId"
|
||||
[formModel]="formModel"
|
||||
[displaySubmit]="false"
|
||||
[displayCancel]="false"
|
||||
(dfChange)="onChange($event)"></ds-form>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{'submission.sections.upload.edit.title' | translate}}</h4>
|
||||
<button type="button" class="close" (click)="onModalClose()" aria-label="Close" [disabled]="isSaving">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<ds-form *ngIf="formModel"
|
||||
#formRef="formComponent"
|
||||
[formId]="formId"
|
||||
[formModel]="formModel"
|
||||
[displaySubmit]="!isSaving"
|
||||
[displayCancel]="!isSaving"
|
||||
(submitForm)="onSubmit()"
|
||||
(cancel)="onModalClose()"
|
||||
(dfChange)="onChange($event)"></ds-form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
import { waitForAsync, ComponentFixture, inject, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@@ -17,19 +17,37 @@ import { SubmissionService } from '../../../../submission.service';
|
||||
import { SubmissionSectionUploadFileEditComponent } from './section-upload-file-edit.component';
|
||||
import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component';
|
||||
import {
|
||||
mockGroup,
|
||||
mockSubmissionCollectionId,
|
||||
mockSubmissionId,
|
||||
mockUploadConfigResponse,
|
||||
mockUploadConfigResponseMetadata,
|
||||
mockUploadFiles
|
||||
mockUploadFiles,
|
||||
mockFileFormData,
|
||||
mockSubmissionObject,
|
||||
} from '../../../../../shared/mocks/submission.mock';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormComponent } from '../../../../../shared/form/form.component';
|
||||
import { FormService } from '../../../../../shared/form/form.service';
|
||||
import { getMockFormService } from '../../../../../shared/mocks/form-service.mock';
|
||||
import { Group } from '../../../../../core/eperson/models/group.model';
|
||||
import { createTestComponent } from '../../../../../shared/testing/utils.test';
|
||||
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { SubmissionJsonPatchOperationsServiceStub } from '../../../../../shared/testing/submission-json-patch-operations-service.stub';
|
||||
import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service';
|
||||
import { SectionUploadService } from '../../section-upload.service';
|
||||
import { getMockSectionUploadService } from '../../../../../shared/mocks/section-upload.service.mock';
|
||||
import { FormFieldMetadataValueObject } from '../../../../../shared/form/builder/models/form-field-metadata-value.model';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
import { dateToISOFormat } from '../../../../../shared/date.util';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
const jsonPatchOpBuilder: any = jasmine.createSpyObj('jsonPatchOpBuilder', {
|
||||
add: jasmine.createSpy('add'),
|
||||
replace: jasmine.createSpy('replace'),
|
||||
remove: jasmine.createSpy('remove'),
|
||||
});
|
||||
|
||||
const formMetadataMock = ['dc.title', 'dc.description'];
|
||||
|
||||
describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
|
||||
@@ -38,7 +56,12 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
let fixture: ComponentFixture<SubmissionSectionUploadFileEditComponent>;
|
||||
let submissionServiceStub: SubmissionServiceStub;
|
||||
let formbuilderService: any;
|
||||
let operationsBuilder: any;
|
||||
let operationsService: any;
|
||||
let formService: any;
|
||||
let uploadService: any;
|
||||
|
||||
const submissionJsonPatchOperationsServiceStub = new SubmissionJsonPatchOperationsServiceStub();
|
||||
const submissionId = mockSubmissionId;
|
||||
const sectionId = 'upload';
|
||||
const collectionId = mockSubmissionCollectionId;
|
||||
@@ -48,6 +71,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
const fileIndex = '0';
|
||||
const fileId = '123456-test-upload';
|
||||
const fileData: any = mockUploadFiles[0];
|
||||
const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex);
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -66,9 +90,15 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
providers: [
|
||||
{ provide: FormService, useValue: getMockFormService() },
|
||||
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
||||
{ provide: SubmissionJsonPatchOperationsService, useValue: submissionJsonPatchOperationsServiceStub },
|
||||
{ provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder },
|
||||
{ provide: SectionUploadService, useValue: getMockSectionUploadService() },
|
||||
FormBuilderService,
|
||||
ChangeDetectorRef,
|
||||
SubmissionSectionUploadFileEditComponent
|
||||
SubmissionSectionUploadFileEditComponent,
|
||||
NgbModal,
|
||||
NgbActiveModal,
|
||||
FormComponent,
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents().then();
|
||||
@@ -114,6 +144,10 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
compAsAny = comp;
|
||||
submissionServiceStub = TestBed.inject(SubmissionService as any);
|
||||
formbuilderService = TestBed.inject(FormBuilderService);
|
||||
operationsBuilder = TestBed.inject(JsonPatchOperationsBuilder);
|
||||
operationsService = TestBed.inject(SubmissionJsonPatchOperationsService);
|
||||
formService = TestBed.inject(FormService);
|
||||
uploadService = TestBed.inject(SectionUploadService);
|
||||
|
||||
comp.submissionId = submissionId;
|
||||
comp.collectionId = collectionId;
|
||||
@@ -123,6 +157,9 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
comp.fileIndex = fileIndex;
|
||||
comp.fileId = fileId;
|
||||
comp.configMetadataForm = configMetadataForm;
|
||||
comp.formMetadata = formMetadataMock;
|
||||
|
||||
formService.isValid.and.returnValue(of(true));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -135,7 +172,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
comp.fileData = fileData;
|
||||
comp.formId = 'testFileForm';
|
||||
|
||||
comp.ngOnChanges();
|
||||
comp.ngOnInit();
|
||||
|
||||
expect(comp.formModel).toBeDefined();
|
||||
expect(comp.formModel.length).toBe(2);
|
||||
@@ -165,7 +202,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
comp.fileData = fileData;
|
||||
comp.formId = 'testFileForm';
|
||||
|
||||
comp.ngOnChanges();
|
||||
comp.ngOnInit();
|
||||
|
||||
const model: DynamicSelectModel<string> = formbuilderService.findById('name', comp.formModel, 0);
|
||||
const formGroup = formbuilderService.createFormGroup(comp.formModel);
|
||||
@@ -186,6 +223,82 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||
comp.setOptions(model, control);
|
||||
expect(formbuilderService.findById).toHaveBeenCalledWith('startDate', (model.parent as DynamicFormArrayGroupModel).group);
|
||||
});
|
||||
|
||||
it('should retrieve Value From Field properly', () => {
|
||||
let field;
|
||||
expect(compAsAny.retrieveValueFromField(field)).toBeUndefined();
|
||||
|
||||
field = new FormFieldMetadataValueObject('test');
|
||||
expect(compAsAny.retrieveValueFromField(field)).toBe('test');
|
||||
|
||||
field = [new FormFieldMetadataValueObject('test')];
|
||||
expect(compAsAny.retrieveValueFromField(field)).toBe('test');
|
||||
});
|
||||
|
||||
it('should save Bitstream File data properly when form is valid', fakeAsync(() => {
|
||||
compAsAny.formRef = {formGroup: null};
|
||||
compAsAny.fileData = fileData;
|
||||
compAsAny.pathCombiner = pathCombiner;
|
||||
formService.validateAllFormFields.and.callFake(() => null);
|
||||
formService.isValid.and.returnValue(of(true));
|
||||
formService.getFormData.and.returnValue(of(mockFileFormData));
|
||||
|
||||
const response = [
|
||||
Object.assign(mockSubmissionObject, {
|
||||
sections: {
|
||||
upload: {
|
||||
files: mockUploadFiles
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
operationsService.jsonPatchByResourceID.and.returnValue(of(response));
|
||||
|
||||
const accessConditionsToSave = [
|
||||
{ name: 'openaccess' },
|
||||
{ name: 'lease', endDate: dateToISOFormat('2019-01-16T00:00:00Z') },
|
||||
{ name: 'embargo', startDate: dateToISOFormat('2019-01-16T00:00:00Z') },
|
||||
];
|
||||
comp.saveBitstreamData();
|
||||
tick();
|
||||
|
||||
let path = 'metadata/dc.title';
|
||||
expect(operationsBuilder.add).toHaveBeenCalledWith(
|
||||
pathCombiner.getPath(path),
|
||||
mockFileFormData.metadata['dc.title'],
|
||||
true
|
||||
);
|
||||
|
||||
path = 'metadata/dc.description';
|
||||
expect(operationsBuilder.add).toHaveBeenCalledWith(
|
||||
pathCombiner.getPath(path),
|
||||
mockFileFormData.metadata['dc.description'],
|
||||
true
|
||||
);
|
||||
|
||||
path = 'accessConditions';
|
||||
expect(operationsBuilder.add).toHaveBeenCalledWith(
|
||||
pathCombiner.getPath(path),
|
||||
accessConditionsToSave,
|
||||
true
|
||||
);
|
||||
|
||||
expect(uploadService.updateFileData).toHaveBeenCalledWith(submissionId, sectionId, mockUploadFiles[0].uuid, mockUploadFiles[0]);
|
||||
|
||||
}));
|
||||
|
||||
it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => {
|
||||
compAsAny.formRef = {formGroup: null};
|
||||
compAsAny.pathCombiner = pathCombiner;
|
||||
formService.validateAllFormFields.and.callFake(() => null);
|
||||
formService.isValid.and.returnValue(of(false));
|
||||
comp.saveBitstreamData();
|
||||
tick();
|
||||
|
||||
expect(uploadService.updateFileData).not.toHaveBeenCalled();
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
import {
|
||||
@@ -32,13 +32,23 @@ import {
|
||||
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
||||
} from './section-upload-file-edit.model';
|
||||
import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component';
|
||||
import { isNotEmpty } from '../../../../../shared/empty.util';
|
||||
import { hasNoValue, hasValue, isNotEmpty, isNotNull } from '../../../../../shared/empty.util';
|
||||
import { SubmissionFormsModel } from '../../../../../core/config/models/config-submission-forms.model';
|
||||
import { FormFieldModel } from '../../../../../shared/form/builder/models/form-field.model';
|
||||
import { AccessConditionOption } from '../../../../../core/config/models/config-access-condition-option.model';
|
||||
import { SubmissionService } from '../../../../submission.service';
|
||||
import { FormService } from '../../../../../shared/form/form.service';
|
||||
import { FormComponent } from '../../../../../shared/form/form.component';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { filter, mergeMap, take } from 'rxjs/operators';
|
||||
import { dateToISOFormat } from '../../../../../shared/date.util';
|
||||
import { SubmissionObject } from '../../../../../core/submission/models/submission-object.model';
|
||||
import { WorkspaceitemSectionUploadObject } from '../../../../../core/submission/models/workspaceitem-section-upload.model';
|
||||
import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
import { SectionUploadService } from '../../section-upload.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
/**
|
||||
* This component represents the edit form for bitstream
|
||||
@@ -48,105 +58,246 @@ import { FormComponent } from '../../../../../shared/form/form.component';
|
||||
styleUrls: ['./section-upload-file-edit.component.scss'],
|
||||
templateUrl: './section-upload-file-edit.component.html',
|
||||
})
|
||||
export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
||||
|
||||
/**
|
||||
* The list of available access condition
|
||||
* @type {Array}
|
||||
*/
|
||||
@Input() availableAccessConditionOptions: any[];
|
||||
|
||||
/**
|
||||
* The submission id
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() collectionId: string;
|
||||
|
||||
/**
|
||||
* Define if collection access conditions policy type :
|
||||
* POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file
|
||||
* POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file
|
||||
* @type {number}
|
||||
*/
|
||||
@Input() collectionPolicyType: number;
|
||||
|
||||
/**
|
||||
* The configuration for the bitstream's metadata form
|
||||
* @type {SubmissionFormsModel}
|
||||
*/
|
||||
@Input() configMetadataForm: SubmissionFormsModel;
|
||||
|
||||
/**
|
||||
* The bitstream's metadata data
|
||||
* @type {WorkspaceitemSectionUploadFileObject}
|
||||
*/
|
||||
@Input() fileData: WorkspaceitemSectionUploadFileObject;
|
||||
|
||||
/**
|
||||
* The bitstream id
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() fileId: string;
|
||||
|
||||
/**
|
||||
* The bitstream array key
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() fileIndex: string;
|
||||
|
||||
/**
|
||||
* The form id
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() formId: string;
|
||||
|
||||
/**
|
||||
* The section id
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() sectionId: string;
|
||||
|
||||
/**
|
||||
* The submission id
|
||||
* @type {string}
|
||||
*/
|
||||
@Input() submissionId: string;
|
||||
|
||||
/**
|
||||
* The form model
|
||||
* @type {DynamicFormControlModel[]}
|
||||
*/
|
||||
public formModel: DynamicFormControlModel[];
|
||||
export class SubmissionSectionUploadFileEditComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The FormComponent reference
|
||||
*/
|
||||
@ViewChild('formRef') public formRef: FormComponent;
|
||||
|
||||
/**
|
||||
* The list of available access condition
|
||||
* @type {Array}
|
||||
*/
|
||||
public availableAccessConditionOptions: any[];
|
||||
|
||||
/**
|
||||
* The submission id
|
||||
* @type {string}
|
||||
*/
|
||||
public collectionId: string;
|
||||
|
||||
/**
|
||||
* Define if collection access conditions policy type :
|
||||
* POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file
|
||||
* POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file
|
||||
* @type {number}
|
||||
*/
|
||||
public collectionPolicyType: number;
|
||||
|
||||
/**
|
||||
* The configuration for the bitstream's metadata form
|
||||
* @type {SubmissionFormsModel}
|
||||
*/
|
||||
public configMetadataForm: SubmissionFormsModel;
|
||||
|
||||
/**
|
||||
* The bitstream's metadata data
|
||||
* @type {WorkspaceitemSectionUploadFileObject}
|
||||
*/
|
||||
public fileData: WorkspaceitemSectionUploadFileObject;
|
||||
|
||||
/**
|
||||
* The bitstream id
|
||||
* @type {string}
|
||||
*/
|
||||
public fileId: string;
|
||||
|
||||
/**
|
||||
* The bitstream array key
|
||||
* @type {string}
|
||||
*/
|
||||
public fileIndex: string;
|
||||
|
||||
/**
|
||||
* The form id
|
||||
* @type {string}
|
||||
*/
|
||||
public formId: string;
|
||||
|
||||
/**
|
||||
* The section id
|
||||
* @type {string}
|
||||
*/
|
||||
public sectionId: string;
|
||||
|
||||
/**
|
||||
* The submission id
|
||||
* @type {string}
|
||||
*/
|
||||
public submissionId: string;
|
||||
|
||||
/**
|
||||
* The list of all available metadata
|
||||
*/
|
||||
formMetadata: string[] = [];
|
||||
|
||||
/**
|
||||
* The form model
|
||||
* @type {DynamicFormControlModel[]}
|
||||
*/
|
||||
formModel: DynamicFormControlModel[];
|
||||
|
||||
/**
|
||||
* When `true` form controls are deactivated
|
||||
*/
|
||||
isSaving = false;
|
||||
|
||||
/**
|
||||
* The [JsonPatchOperationPathCombiner] object
|
||||
* @type {JsonPatchOperationPathCombiner}
|
||||
*/
|
||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||
|
||||
protected subscriptions: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param activeModal
|
||||
* @param {ChangeDetectorRef} cdr
|
||||
* @param {FormBuilderService} formBuilderService
|
||||
* @param {FormService} formService
|
||||
* @param {SubmissionService} submissionService
|
||||
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||
* @param {SectionUploadService} uploadService
|
||||
*/
|
||||
constructor(private cdr: ChangeDetectorRef,
|
||||
private formBuilderService: FormBuilderService,
|
||||
private formService: FormService,
|
||||
private submissionService: SubmissionService) {
|
||||
constructor(
|
||||
protected activeModal: NgbActiveModal,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private formBuilderService: FormBuilderService,
|
||||
private formService: FormService,
|
||||
private submissionService: SubmissionService,
|
||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||
private operationsService: SubmissionJsonPatchOperationsService,
|
||||
private uploadService: SectionUploadService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form model values
|
||||
*
|
||||
* @param formModel
|
||||
* The form model
|
||||
*/
|
||||
public initModelData(formModel: DynamicFormControlModel[]) {
|
||||
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
||||
Array.of('name', 'startDate', 'endDate')
|
||||
.filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key]))
|
||||
.forEach((key) => {
|
||||
const metadataModel: any = this.formBuilderService.findById(key, formModel, index);
|
||||
if (metadataModel) {
|
||||
if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) {
|
||||
const date = new Date(accessCondition[key]);
|
||||
metadataModel.value = {
|
||||
year: date.getUTCFullYear(),
|
||||
month: date.getUTCMonth() + 1,
|
||||
day: date.getUTCDate()
|
||||
};
|
||||
} else {
|
||||
metadataModel.value = accessCondition[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch form model update when changing an access condition
|
||||
*
|
||||
* @param event
|
||||
* The event emitted
|
||||
*/
|
||||
onChange(event: DynamicFormControlEvent) {
|
||||
if (event.model.id === 'name') {
|
||||
this.setOptions(event.model, event.control);
|
||||
}
|
||||
}
|
||||
|
||||
onModalClose() {
|
||||
this.activeModal.dismiss();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.isSaving = true;
|
||||
this.saveBitstreamData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update `startDate`, 'groupUUID' and 'endDate' model
|
||||
*
|
||||
* @param model
|
||||
* The [[DynamicFormControlModel]] object
|
||||
* @param control
|
||||
* The [[FormControl]] object
|
||||
*/
|
||||
public setOptions(model: DynamicFormControlModel, control: FormControl) {
|
||||
let accessCondition: AccessConditionOption = null;
|
||||
this.availableAccessConditionOptions.filter((element) => element.name === control.value)
|
||||
.forEach((element) => accessCondition = element );
|
||||
if (isNotEmpty(accessCondition)) {
|
||||
const showGroups: boolean = accessCondition.hasStartDate === true || accessCondition.hasEndDate === true;
|
||||
|
||||
const startDateControl: FormControl = control.parent.get('startDate') as FormControl;
|
||||
const endDateControl: FormControl = control.parent.get('endDate') as FormControl;
|
||||
|
||||
// Clear previous state
|
||||
startDateControl?.markAsUntouched();
|
||||
endDateControl?.markAsUntouched();
|
||||
|
||||
startDateControl?.setValue(null);
|
||||
control.parent.markAsDirty();
|
||||
endDateControl?.setValue(null);
|
||||
|
||||
if (showGroups) {
|
||||
if (accessCondition.hasStartDate) {
|
||||
const startDateModel = this.formBuilderService.findById(
|
||||
'startDate',
|
||||
(model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel;
|
||||
|
||||
const min = new Date(accessCondition.maxStartDate);
|
||||
startDateModel.max = {
|
||||
year: min.getUTCFullYear(),
|
||||
month: min.getUTCMonth() + 1,
|
||||
day: min.getUTCDate()
|
||||
};
|
||||
}
|
||||
if (accessCondition.hasEndDate) {
|
||||
const endDateModel = this.formBuilderService.findById(
|
||||
'endDate',
|
||||
(model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel;
|
||||
|
||||
const max = new Date(accessCondition.maxEndDate);
|
||||
endDateModel.max = {
|
||||
year: max.getUTCFullYear(),
|
||||
month: max.getUTCMonth() + 1,
|
||||
day: max.getUTCDate()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch form model init
|
||||
*/
|
||||
ngOnChanges() {
|
||||
ngOnInit() {
|
||||
if (this.fileData && this.formId) {
|
||||
this.formModel = this.buildFileEditForm();
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.unsubscribeAll();
|
||||
}
|
||||
|
||||
protected retrieveValueFromField(field: any) {
|
||||
const temp = Array.isArray(field) ? field[0] : field;
|
||||
return (temp) ? temp.value : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form model
|
||||
*/
|
||||
@@ -193,17 +344,17 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
||||
const showEnd: boolean = condition.hasEndDate === true;
|
||||
const showGroups: boolean = showStart || showEnd;
|
||||
if (showStart) {
|
||||
hasStart.push({ id: 'name', value: condition.name });
|
||||
hasStart.push({id: 'name', value: condition.name});
|
||||
}
|
||||
if (showEnd) {
|
||||
hasEnd.push({ id: 'name', value: condition.name });
|
||||
hasEnd.push({id: 'name', value: condition.name});
|
||||
}
|
||||
if (showGroups) {
|
||||
hasGroups.push({ id: 'name', value: condition.name });
|
||||
hasGroups.push({id: 'name', value: condition.name});
|
||||
}
|
||||
});
|
||||
const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart }] };
|
||||
const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd }] };
|
||||
const confStart = {relations: [{match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart}]};
|
||||
const confEnd = {relations: [{match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd}]};
|
||||
|
||||
accessConditionsArrayConfig.groupFactory = () => {
|
||||
const type = new DynamicSelectModel(accessConditionTypeModelConfig, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT);
|
||||
@@ -213,7 +364,9 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
||||
const startDate = new DynamicDatePickerModel(startDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT);
|
||||
const endDate = new DynamicDatePickerModel(endDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT);
|
||||
const accessConditionGroupConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG);
|
||||
accessConditionGroupConfig.group = [type, startDate, endDate];
|
||||
accessConditionGroupConfig.group = [type];
|
||||
if (hasStart.length > 0) { accessConditionGroupConfig.group.push(startDate); }
|
||||
if (hasEnd.length > 0) { accessConditionGroupConfig.group.push(endDate); }
|
||||
return [new DynamicFormGroupModel(accessConditionGroupConfig, BITSTREAM_ACCESS_CONDITION_GROUP_LAYOUT)];
|
||||
};
|
||||
|
||||
@@ -229,98 +382,95 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form model values
|
||||
*
|
||||
* @param formModel
|
||||
* The form model
|
||||
* Save bitstream metadata
|
||||
*/
|
||||
public initModelData(formModel: DynamicFormControlModel[]) {
|
||||
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
||||
Array.of('name', 'startDate', 'endDate')
|
||||
.filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key]))
|
||||
.forEach((key) => {
|
||||
const metadataModel: any = this.formBuilderService.findById(key, formModel, index);
|
||||
if (metadataModel) {
|
||||
if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) {
|
||||
const date = new Date(accessCondition[key]);
|
||||
metadataModel.value = {
|
||||
year: date.getUTCFullYear(),
|
||||
month: date.getUTCMonth() + 1,
|
||||
day: date.getUTCDate()
|
||||
};
|
||||
} else {
|
||||
metadataModel.value = accessCondition[key];
|
||||
saveBitstreamData() {
|
||||
// validate form
|
||||
this.formService.validateAllFormFields(this.formRef.formGroup);
|
||||
const saveBitstreamDataSubscription = this.formService.isValid(this.formId).pipe(
|
||||
take(1),
|
||||
filter((isValid) => isValid),
|
||||
mergeMap(() => this.formService.getFormData(this.formId)),
|
||||
take(1),
|
||||
mergeMap((formData: any) => {
|
||||
// collect bitstream metadata
|
||||
Object.keys((formData.metadata))
|
||||
.filter((key) => isNotEmpty(formData.metadata[key]))
|
||||
.forEach((key) => {
|
||||
const metadataKey = key.replace(/_/g, '.');
|
||||
const path = `metadata/${metadataKey}`;
|
||||
this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true);
|
||||
});
|
||||
Object.keys((this.fileData.metadata))
|
||||
.filter((key) => isNotEmpty(this.fileData.metadata[key]))
|
||||
.filter((key) => hasNoValue(formData.metadata[key]))
|
||||
.filter((key) => this.formMetadata.includes(key))
|
||||
.forEach((key) => {
|
||||
const metadataKey = key.replace(/_/g, '.');
|
||||
const path = `metadata/${metadataKey}`;
|
||||
this.operationsBuilder.remove(this.pathCombiner.getPath(path));
|
||||
});
|
||||
const accessConditionsToSave = [];
|
||||
formData.accessConditions
|
||||
.map((accessConditions) => accessConditions.accessConditionGroup)
|
||||
.filter((accessCondition) => isNotEmpty(accessCondition))
|
||||
.forEach((accessCondition) => {
|
||||
let accessConditionOpt;
|
||||
|
||||
this.availableAccessConditionOptions
|
||||
.filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value)
|
||||
.forEach((element) => accessConditionOpt = element);
|
||||
|
||||
if (accessConditionOpt) {
|
||||
const currentAccessCondition = Object.assign({}, accessCondition);
|
||||
currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name);
|
||||
|
||||
/* When start and end date fields are deactivated, their values may be still present in formData,
|
||||
therefore it is necessary to delete them if they're not allowed by the current access condition option. */
|
||||
if (!accessConditionOpt.hasStartDate) {
|
||||
delete currentAccessCondition.startDate;
|
||||
} else if (accessCondition.startDate) {
|
||||
const startDate = this.retrieveValueFromField(accessCondition.startDate);
|
||||
currentAccessCondition.startDate = dateToISOFormat(startDate);
|
||||
}
|
||||
if (!accessConditionOpt.hasEndDate) {
|
||||
delete currentAccessCondition.endDate;
|
||||
} else if (accessCondition.endDate) {
|
||||
const endDate = this.retrieveValueFromField(accessCondition.endDate);
|
||||
currentAccessCondition.endDate = dateToISOFormat(endDate);
|
||||
}
|
||||
accessConditionsToSave.push(currentAccessCondition);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Dispatch form model update when changing an access condition
|
||||
*
|
||||
* @param event
|
||||
* The event emitted
|
||||
*/
|
||||
public onChange(event: DynamicFormControlEvent) {
|
||||
if (event.model.id === 'name') {
|
||||
this.setOptions(event.model, event.control);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update `startDate`, 'groupUUID' and 'endDate' model
|
||||
*
|
||||
* @param model
|
||||
* The [[DynamicFormControlModel]] object
|
||||
* @param control
|
||||
* The [[FormControl]] object
|
||||
*/
|
||||
public setOptions(model: DynamicFormControlModel, control: FormControl) {
|
||||
let accessCondition: AccessConditionOption = null;
|
||||
this.availableAccessConditionOptions.filter((element) => element.name === control.value)
|
||||
.forEach((element) => accessCondition = element);
|
||||
if (isNotEmpty(accessCondition)) {
|
||||
const showGroups: boolean = accessCondition.hasStartDate === true || accessCondition.hasEndDate === true;
|
||||
|
||||
const startDateControl: FormControl = control.parent.get('startDate') as FormControl;
|
||||
const endDateControl: FormControl = control.parent.get('endDate') as FormControl;
|
||||
|
||||
// Clear previous state
|
||||
startDateControl.markAsUntouched();
|
||||
endDateControl.markAsUntouched();
|
||||
|
||||
startDateControl.setValue(null);
|
||||
control.parent.markAsDirty();
|
||||
endDateControl.setValue(null);
|
||||
|
||||
if (showGroups) {
|
||||
if (accessCondition.hasStartDate) {
|
||||
const startDateModel = this.formBuilderService.findById(
|
||||
'startDate',
|
||||
(model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel;
|
||||
|
||||
const min = new Date(accessCondition.maxStartDate);
|
||||
startDateModel.max = {
|
||||
year: min.getUTCFullYear(),
|
||||
month: min.getUTCMonth() + 1,
|
||||
day: min.getUTCDate()
|
||||
};
|
||||
if (isNotEmpty(accessConditionsToSave)) {
|
||||
this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true);
|
||||
}
|
||||
if (accessCondition.hasEndDate) {
|
||||
const endDateModel = this.formBuilderService.findById(
|
||||
'endDate',
|
||||
(model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel;
|
||||
|
||||
const max = new Date(accessCondition.maxEndDate);
|
||||
endDateModel.max = {
|
||||
year: max.getUTCFullYear(),
|
||||
month: max.getUTCMonth() + 1,
|
||||
day: max.getUTCDate()
|
||||
};
|
||||
}
|
||||
// dispatch a PATCH request to save metadata
|
||||
return this.operationsService.jsonPatchByResourceID(
|
||||
this.submissionService.getSubmissionObjectLinkName(),
|
||||
this.submissionId,
|
||||
this.pathCombiner.rootElement,
|
||||
this.pathCombiner.subRootElement);
|
||||
})
|
||||
).subscribe((result: SubmissionObject[]) => {
|
||||
if (result[0].sections[this.sectionId]) {
|
||||
const uploadSection = (result[0].sections[this.sectionId] as WorkspaceitemSectionUploadObject);
|
||||
Object.keys(uploadSection.files)
|
||||
.filter((key) => uploadSection.files[key].uuid === this.fileId)
|
||||
.forEach((key) => this.uploadService.updateFileData(
|
||||
this.submissionId, this.sectionId, this.fileId, uploadSection.files[key])
|
||||
);
|
||||
}
|
||||
}
|
||||
this.isSaving = false;
|
||||
this.activeModal.close();
|
||||
});
|
||||
this.subscriptions.push(saveBitstreamDataSubscription);
|
||||
}
|
||||
|
||||
private unsubscribeAll() {
|
||||
this.subscriptions.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -8,15 +8,15 @@
|
||||
<div class="float-left w-75">
|
||||
<h3>{{fileName}} <span class="text-muted">({{fileData?.sizeBytes | dsFileSize}})</span></h3>
|
||||
</div>
|
||||
<div class="float-right w-15" [class.sticky-buttons]="!readMode">
|
||||
<ng-container *ngIf="readMode">
|
||||
<div class="float-right w-15">
|
||||
<ng-container>
|
||||
<ds-file-download-link [cssClasses]="'btn btn-link-focus'" [isBlank]="true" [bitstream]="getBitstream()" [enableRequestACopy]="false">
|
||||
<i class="fa fa-download fa-2x text-normal" aria-hidden="true"></i>
|
||||
</ds-file-download-link>
|
||||
<button class="btn btn-link-focus"
|
||||
[attr.aria-label]="'submission.sections.upload.edit.title' | translate"
|
||||
title="{{ 'submission.sections.upload.edit.title' | translate }}"
|
||||
(click)="$event.preventDefault();switchMode();">
|
||||
(click)="$event.preventDefault();editBitstreamData();">
|
||||
<i class="fa fa-edit fa-2x text-normal"></i>
|
||||
</button>
|
||||
<button class="btn btn-link-focus"
|
||||
@@ -28,40 +28,9 @@
|
||||
<i *ngIf="!(processingDelete$ | async)" class="fa fa-trash fa-2x text-danger"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!readMode">
|
||||
<button class="btn btn-link-focus"
|
||||
[attr.aria-label]="'submission.sections.upload.save-metadata' | translate"
|
||||
title="{{ 'submission.sections.upload.save-metadata' | translate }}"
|
||||
(click)="saveBitstreamData($event);">
|
||||
<i class="fa fa-save fa-2x text-success"></i>
|
||||
</button>
|
||||
<button class="btn btn-link-focus"
|
||||
[attr.aria-label]="'submission.sections.upload.undo' | translate"
|
||||
title="{{ 'submission.sections.upload.undo' | translate }}"
|
||||
(click)="$event.preventDefault();switchMode();"><i class="fa fa-ban fa-2x text-warning"></i></button>
|
||||
<button class="btn btn-link-focus"
|
||||
[attr.aria-label]="'submission.sections.upload.delete.confirm.title' | translate"
|
||||
title="{{ 'submission.sections.upload.delete.confirm.title' | translate }}"
|
||||
[disabled]="(processingDelete$ | async)"
|
||||
(click)="$event.preventDefault();confirmDelete(content);">
|
||||
<i *ngIf="(processingDelete$ | async)" class="fas fa-circle-notch fa-spin fa-2x text-danger"></i>
|
||||
<i *ngIf="!(processingDelete$ | async)" class="fa fa-trash fa-2x text-danger"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<ds-submission-section-upload-file-view *ngIf="readMode"
|
||||
[fileData]="fileData"></ds-submission-section-upload-file-view>
|
||||
<ds-submission-section-upload-file-edit *ngIf="!readMode"
|
||||
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
||||
[collectionId]="collectionId"
|
||||
[collectionPolicyType]="collectionPolicyType"
|
||||
[configMetadataForm]="configMetadataForm"
|
||||
[fileData]="fileData"
|
||||
[fileId]="fileId"
|
||||
[fileIndex]="fileIndex"
|
||||
[formId]="formId"
|
||||
[sectionId]="sectionId"></ds-submission-section-upload-file-edit>
|
||||
<ds-submission-section-upload-file-view [fileData]="fileData"></ds-submission-section-upload-file-view>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@@ -1,6 +0,0 @@
|
||||
.sticky-buttons {
|
||||
position: sticky;
|
||||
top: calc(var(--bs-dropdown-item-padding-x) * 3);
|
||||
z-index: var(--ds-submission-footer-z-index);
|
||||
background-color: rgba(255, 255, 255, .97);
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||
import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { BrowserModule, By } from '@angular/platform-browser';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { of, of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { FormService } from '../../../../shared/form/form.service';
|
||||
@@ -17,10 +17,8 @@ import { SubmissionJsonPatchOperationsService } from '../../../../core/submissio
|
||||
import { SubmissionSectionUploadFileComponent } from './section-upload-file.component';
|
||||
import { SubmissionServiceStub } from '../../../../shared/testing/submission-service.stub';
|
||||
import {
|
||||
mockFileFormData,
|
||||
mockSubmissionCollectionId,
|
||||
mockSubmissionId,
|
||||
mockSubmissionObject,
|
||||
mockUploadConfigResponse,
|
||||
mockUploadFiles
|
||||
} from '../../../../shared/mocks/submission.mock';
|
||||
@@ -32,10 +30,19 @@ import { FileSizePipe } from '../../../../shared/utils/file-size-pipe';
|
||||
import { POLICY_DEFAULT_WITH_LIST } from '../section-upload.component';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
import { getMockSectionUploadService } from '../../../../shared/mocks/section-upload.service.mock';
|
||||
import { FormFieldMetadataValueObject } from '../../../../shared/form/builder/models/form-field-metadata-value.model';
|
||||
import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
|
||||
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
|
||||
import { dateToISOFormat } from '../../../../shared/date.util';
|
||||
|
||||
const configMetadataFormMock = {
|
||||
rows: [{
|
||||
fields: [{
|
||||
selectableMetadata: [
|
||||
{metadata: 'dc.title', label: null, closed: false},
|
||||
{metadata: 'dc.description', label: null, closed: false}
|
||||
]
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
describe('SubmissionSectionUploadFileComponent test suite', () => {
|
||||
|
||||
@@ -117,6 +124,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
|
||||
|
||||
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
||||
testComp = testFixture.componentInstance;
|
||||
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -124,9 +132,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
|
||||
});
|
||||
|
||||
it('should create SubmissionSectionUploadFileComponent', inject([SubmissionSectionUploadFileComponent], (app: SubmissionSectionUploadFileComponent) => {
|
||||
|
||||
expect(app).toBeDefined();
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -135,6 +141,7 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
|
||||
fixture = TestBed.createComponent(SubmissionSectionUploadFileComponent);
|
||||
comp = fixture.componentInstance;
|
||||
compAsAny = comp;
|
||||
compAsAny.configMetadataForm = configMetadataFormMock;
|
||||
submissionServiceStub = TestBed.inject(SubmissionService as any);
|
||||
uploadService = TestBed.inject(SectionUploadService);
|
||||
formService = TestBed.inject(FormService);
|
||||
@@ -210,96 +217,20 @@ describe('SubmissionSectionUploadFileComponent test suite', () => {
|
||||
pathCombiner.subRootElement);
|
||||
});
|
||||
|
||||
it('should save Bitstream File data properly when form is valid', fakeAsync(() => {
|
||||
compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent);
|
||||
compAsAny.fileEditComp.formRef = {formGroup: null};
|
||||
compAsAny.pathCombiner = pathCombiner;
|
||||
const event = new Event('click', null);
|
||||
spyOn(comp, 'switchMode');
|
||||
formService.validateAllFormFields.and.callFake(() => null);
|
||||
formService.isValid.and.returnValue(observableOf(true));
|
||||
formService.getFormData.and.returnValue(observableOf(mockFileFormData));
|
||||
it('should open edit modal when edit button is clicked', () => {
|
||||
spyOn(compAsAny, 'editBitstreamData').and.callThrough();
|
||||
comp.fileData = fileData;
|
||||
|
||||
const response = [
|
||||
Object.assign(mockSubmissionObject, {
|
||||
sections: {
|
||||
upload: {
|
||||
files: mockUploadFiles
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
operationsService.jsonPatchByResourceID.and.returnValue(observableOf(response));
|
||||
fixture.detectChanges();
|
||||
|
||||
const accessConditionsToSave = [
|
||||
{ name: 'openaccess' },
|
||||
{ name: 'lease', endDate: dateToISOFormat('2019-01-16T00:00:00Z') },
|
||||
{ name: 'embargo', startDate: dateToISOFormat('2019-01-16T00:00:00Z') },
|
||||
];
|
||||
comp.saveBitstreamData(event);
|
||||
tick();
|
||||
const modalBtn = fixture.debugElement.query(By.css('.fa-edit '));
|
||||
|
||||
let path = 'metadata/dc.title';
|
||||
expect(operationsBuilder.add).toHaveBeenCalledWith(
|
||||
pathCombiner.getPath(path),
|
||||
mockFileFormData.metadata['dc.title'],
|
||||
true
|
||||
);
|
||||
modalBtn.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
path = 'metadata/dc.description';
|
||||
expect(operationsBuilder.add).toHaveBeenCalledWith(
|
||||
pathCombiner.getPath(path),
|
||||
mockFileFormData.metadata['dc.description'],
|
||||
true
|
||||
);
|
||||
|
||||
path = 'accessConditions';
|
||||
expect(operationsBuilder.add).toHaveBeenCalledWith(
|
||||
pathCombiner.getPath(path),
|
||||
accessConditionsToSave,
|
||||
true
|
||||
);
|
||||
|
||||
expect(comp.switchMode).toHaveBeenCalled();
|
||||
expect(uploadService.updateFileData).toHaveBeenCalledWith(submissionId, sectionId, mockUploadFiles[0].uuid, mockUploadFiles[0]);
|
||||
|
||||
}));
|
||||
|
||||
it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => {
|
||||
compAsAny.fileEditComp = TestBed.inject(SubmissionSectionUploadFileEditComponent);
|
||||
compAsAny.fileEditComp.formRef = {formGroup: null};
|
||||
compAsAny.pathCombiner = pathCombiner;
|
||||
const event = new Event('click', null);
|
||||
spyOn(comp, 'switchMode');
|
||||
formService.validateAllFormFields.and.callFake(() => null);
|
||||
formService.isValid.and.returnValue(observableOf(false));
|
||||
|
||||
expect(comp.switchMode).not.toHaveBeenCalled();
|
||||
expect(uploadService.updateFileData).not.toHaveBeenCalled();
|
||||
|
||||
}));
|
||||
|
||||
it('should retrieve Value From Field properly', () => {
|
||||
let field;
|
||||
expect(compAsAny.retrieveValueFromField(field)).toBeUndefined();
|
||||
|
||||
field = new FormFieldMetadataValueObject('test');
|
||||
expect(compAsAny.retrieveValueFromField(field)).toBe('test');
|
||||
|
||||
field = [new FormFieldMetadataValueObject('test')];
|
||||
expect(compAsAny.retrieveValueFromField(field)).toBe('test');
|
||||
expect(compAsAny.editBitstreamData).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should switch read mode', () => {
|
||||
comp.readMode = false;
|
||||
|
||||
comp.switchMode();
|
||||
expect(comp.readMode).toBeTruthy();
|
||||
|
||||
comp.switchMode();
|
||||
|
||||
expect(comp.readMode).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -314,7 +245,7 @@ class TestComponent {
|
||||
availableAccessConditionOptions;
|
||||
collectionId = mockSubmissionCollectionId;
|
||||
collectionPolicyType;
|
||||
configMetadataForm$;
|
||||
configMetadataForm$ = of(configMetadataFormMock);
|
||||
fileIndexes = [];
|
||||
fileList = [];
|
||||
fileNames = [];
|
||||
|
@@ -1,25 +1,23 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { filter, mergeMap, take } from 'rxjs/operators';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { DynamicFormControlModel, } from '@ng-dynamic-forms/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import { SectionUploadService } from '../section-upload.service';
|
||||
import { isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util';
|
||||
import { hasValue, isNotUndefined } from '../../../../shared/empty.util';
|
||||
import { FormService } from '../../../../shared/form/form.service';
|
||||
import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
import { WorkspaceitemSectionUploadFileObject } from '../../../../core/submission/models/workspaceitem-section-upload-file.model';
|
||||
import { SubmissionFormsModel } from '../../../../core/config/models/config-submission-forms.model';
|
||||
import { dateToISOFormat } from '../../../../shared/date.util';
|
||||
import { SubmissionService } from '../../../submission.service';
|
||||
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||
import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.service';
|
||||
import { SubmissionObject } from '../../../../core/submission/models/submission-object.model';
|
||||
import { WorkspaceitemSectionUploadObject } from '../../../../core/submission/models/workspaceitem-section-upload.model';
|
||||
import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
|
||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||
import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config';
|
||||
|
||||
/**
|
||||
* This component represents a single bitstream contained in the submission
|
||||
@@ -87,6 +85,13 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
||||
*/
|
||||
@Input() submissionId: string;
|
||||
|
||||
/**
|
||||
* The [[SubmissionSectionUploadFileEditComponent]] reference
|
||||
* @type {SubmissionSectionUploadFileEditComponent}
|
||||
*/
|
||||
@ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent;
|
||||
|
||||
|
||||
/**
|
||||
* The bitstream's metadata data
|
||||
* @type {WorkspaceitemSectionUploadFileObject}
|
||||
@@ -130,10 +135,10 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
||||
protected subscriptions: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* The [[SubmissionSectionUploadFileEditComponent]] reference
|
||||
* @type {SubmissionSectionUploadFileEditComponent}
|
||||
* Array containing all the form metadata defined in configMetadataForm
|
||||
* @type {Array}
|
||||
*/
|
||||
@ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent;
|
||||
protected formMetadata: string[] = [];
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
@@ -147,14 +152,16 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
||||
* @param {SubmissionService} submissionService
|
||||
* @param {SectionUploadService} uploadService
|
||||
*/
|
||||
constructor(private cdr: ChangeDetectorRef,
|
||||
private formService: FormService,
|
||||
private halService: HALEndpointService,
|
||||
private modalService: NgbModal,
|
||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||
private operationsService: SubmissionJsonPatchOperationsService,
|
||||
private submissionService: SubmissionService,
|
||||
private uploadService: SectionUploadService) {
|
||||
constructor(
|
||||
private cdr: ChangeDetectorRef,
|
||||
private formService: FormService,
|
||||
private halService: HALEndpointService,
|
||||
private modalService: NgbModal,
|
||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||
private operationsService: SubmissionJsonPatchOperationsService,
|
||||
private submissionService: SubmissionService,
|
||||
private uploadService: SectionUploadService,
|
||||
) {
|
||||
this.readMode = true;
|
||||
}
|
||||
|
||||
@@ -182,22 +189,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
||||
ngOnInit() {
|
||||
this.formId = this.formService.getUniqueId(this.fileId);
|
||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId, 'files', this.fileIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete bitstream from submission
|
||||
*/
|
||||
protected deleteFile() {
|
||||
this.operationsBuilder.remove(this.pathCombiner.getPath());
|
||||
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
|
||||
this.submissionService.getSubmissionObjectLinkName(),
|
||||
this.submissionId,
|
||||
this.pathCombiner.rootElement,
|
||||
this.pathCombiner.subRootElement)
|
||||
.subscribe(() => {
|
||||
this.uploadService.removeUploadedFile(this.submissionId, this.sectionId, this.fileId);
|
||||
this.processingDelete$.next(false);
|
||||
}));
|
||||
this.loadFormMetadata();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,98 +217,63 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bitstream metadata
|
||||
*
|
||||
* @param event
|
||||
* the click event emitted
|
||||
*/
|
||||
public saveBitstreamData(event) {
|
||||
event.preventDefault();
|
||||
editBitstreamData() {
|
||||
|
||||
// validate form
|
||||
this.formService.validateAllFormFields(this.fileEditComp.formRef.formGroup);
|
||||
this.subscriptions.push(this.formService.isValid(this.formId).pipe(
|
||||
take(1),
|
||||
filter((isValid) => isValid),
|
||||
mergeMap(() => this.formService.getFormData(this.formId)),
|
||||
take(1),
|
||||
mergeMap((formData: any) => {
|
||||
// collect bitstream metadata
|
||||
Object.keys((formData.metadata))
|
||||
.filter((key) => isNotEmpty(formData.metadata[key]))
|
||||
.forEach((key) => {
|
||||
const metadataKey = key.replace(/_/g, '.');
|
||||
const path = `metadata/${metadataKey}`;
|
||||
this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true);
|
||||
const options: NgbModalOptions = {
|
||||
size: 'xl',
|
||||
backdrop: 'static',
|
||||
};
|
||||
|
||||
const activeModal = this.modalService.open(SubmissionSectionUploadFileEditComponent, options);
|
||||
|
||||
activeModal.componentInstance.availableAccessConditionOptions = this.availableAccessConditionOptions;
|
||||
activeModal.componentInstance.collectionId = this.collectionId;
|
||||
activeModal.componentInstance.collectionPolicyType = this.collectionPolicyType;
|
||||
activeModal.componentInstance.configMetadataForm = this.configMetadataForm;
|
||||
activeModal.componentInstance.fileData = this.fileData;
|
||||
activeModal.componentInstance.fileId = this.fileId;
|
||||
activeModal.componentInstance.fileIndex = this.fileIndex;
|
||||
activeModal.componentInstance.formId = this.formId;
|
||||
activeModal.componentInstance.sectionId = this.sectionId;
|
||||
activeModal.componentInstance.formMetadata = this.formMetadata;
|
||||
activeModal.componentInstance.pathCombiner = this.pathCombiner;
|
||||
activeModal.componentInstance.submissionId = this.submissionId;
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.unsubscribeAll();
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
this.subscriptions.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||
}
|
||||
|
||||
protected loadFormMetadata() {
|
||||
this.configMetadataForm.rows.forEach((row) => {
|
||||
row.fields.forEach((field) => {
|
||||
field.selectableMetadata.forEach((metadatum) => {
|
||||
this.formMetadata.push(metadatum.metadata);
|
||||
});
|
||||
const accessConditionsToSave = [];
|
||||
formData.accessConditions
|
||||
.map((accessConditions) => accessConditions.accessConditionGroup)
|
||||
.filter((accessCondition) => isNotEmpty(accessCondition))
|
||||
.forEach((accessCondition) => {
|
||||
let accessConditionOpt;
|
||||
|
||||
this.availableAccessConditionOptions
|
||||
.filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value)
|
||||
.forEach((element) => accessConditionOpt = element);
|
||||
|
||||
if (accessConditionOpt) {
|
||||
accessConditionOpt = Object.assign({}, accessCondition);
|
||||
accessConditionOpt.name = this.retrieveValueFromField(accessCondition.name);
|
||||
if (accessCondition.startDate) {
|
||||
const startDate = this.retrieveValueFromField(accessCondition.startDate);
|
||||
accessConditionOpt.startDate = dateToISOFormat(startDate);
|
||||
}
|
||||
if (accessCondition.endDate) {
|
||||
const endDate = this.retrieveValueFromField(accessCondition.endDate);
|
||||
accessConditionOpt.endDate = dateToISOFormat(endDate);
|
||||
}
|
||||
accessConditionsToSave.push(accessConditionOpt);
|
||||
}
|
||||
});
|
||||
|
||||
if (isNotEmpty(accessConditionsToSave)) {
|
||||
this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true);
|
||||
}
|
||||
|
||||
// dispatch a PATCH request to save metadata
|
||||
return this.operationsService.jsonPatchByResourceID(
|
||||
this.submissionService.getSubmissionObjectLinkName(),
|
||||
this.submissionId,
|
||||
this.pathCombiner.rootElement,
|
||||
this.pathCombiner.subRootElement);
|
||||
})
|
||||
).subscribe((result: SubmissionObject[]) => {
|
||||
if (result[0].sections[this.sectionId]) {
|
||||
const uploadSection = (result[0].sections[this.sectionId] as WorkspaceitemSectionUploadObject);
|
||||
Object.keys(uploadSection.files)
|
||||
.filter((key) => uploadSection.files[key].uuid === this.fileId)
|
||||
.forEach((key) => this.uploadService.updateFileData(
|
||||
this.submissionId, this.sectionId, this.fileId, uploadSection.files[key])
|
||||
);
|
||||
});
|
||||
}
|
||||
this.switchMode();
|
||||
}));
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve field value
|
||||
*
|
||||
* @param field
|
||||
* the specified field object
|
||||
* Delete bitstream from submission
|
||||
*/
|
||||
private retrieveValueFromField(field: any) {
|
||||
const temp = Array.isArray(field) ? field[0] : field;
|
||||
return (temp) ? temp.value : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch from edit form to metadata view
|
||||
*/
|
||||
public switchMode() {
|
||||
this.readMode = !this.readMode;
|
||||
this.cdr.detectChanges();
|
||||
protected deleteFile() {
|
||||
this.operationsBuilder.remove(this.pathCombiner.getPath());
|
||||
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
|
||||
this.submissionService.getSubmissionObjectLinkName(),
|
||||
this.submissionId,
|
||||
this.pathCombiner.rootElement,
|
||||
this.pathCombiner.subRootElement)
|
||||
.subscribe(() => {
|
||||
this.uploadService.removeUploadedFile(this.submissionId, this.sectionId, this.fileId);
|
||||
this.processingDelete$.next(false);
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user