mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
[CST-4884] Bitstream edit form moved inside modal (test TBD)
This commit is contained in:
@@ -1,9 +1,21 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<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"
|
<ds-form *ngIf="formModel"
|
||||||
#formRef="formComponent"
|
#formRef="formComponent"
|
||||||
[formId]="formId"
|
[formId]="formId"
|
||||||
[formModel]="formModel"
|
[formModel]="formModel"
|
||||||
[displaySubmit]="false"
|
[displaySubmit]="!isSaving"
|
||||||
[displayCancel]="false"
|
[displayCancel]="!isSaving"
|
||||||
|
(submitForm)="onSubmit()"
|
||||||
|
(cancel)="onModalClose()"
|
||||||
(dfChange)="onChange($event)"></ds-form>
|
(dfChange)="onChange($event)"></ds-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -32,13 +32,23 @@ import {
|
|||||||
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
||||||
} from './section-upload-file-edit.model';
|
} from './section-upload-file-edit.model';
|
||||||
import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component';
|
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 { SubmissionFormsModel } from '../../../../../core/config/models/config-submission-forms.model';
|
||||||
import { FormFieldModel } from '../../../../../shared/form/builder/models/form-field.model';
|
import { FormFieldModel } from '../../../../../shared/form/builder/models/form-field.model';
|
||||||
import { AccessConditionOption } from '../../../../../core/config/models/config-access-condition-option.model';
|
import { AccessConditionOption } from '../../../../../core/config/models/config-access-condition-option.model';
|
||||||
import { SubmissionService } from '../../../../submission.service';
|
import { SubmissionService } from '../../../../submission.service';
|
||||||
import { FormService } from '../../../../../shared/form/form.service';
|
import { FormService } from '../../../../../shared/form/form.service';
|
||||||
import { FormComponent } from '../../../../../shared/form/form.component';
|
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
|
* This component represents the edit form for bitstream
|
||||||
@@ -48,7 +58,31 @@ import { FormComponent } from '../../../../../shared/form/form.component';
|
|||||||
styleUrls: ['./section-upload-file-edit.component.scss'],
|
styleUrls: ['./section-upload-file-edit.component.scss'],
|
||||||
templateUrl: './section-upload-file-edit.component.html',
|
templateUrl: './section-upload-file-edit.component.html',
|
||||||
})
|
})
|
||||||
export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
export class SubmissionSectionUploadFileEditComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(
|
||||||
|
protected activeModal: NgbActiveModal,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private formBuilderService: FormBuilderService,
|
||||||
|
private formService: FormService,
|
||||||
|
private submissionService: SubmissionService,
|
||||||
|
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||||
|
private operationsService: SubmissionJsonPatchOperationsService,
|
||||||
|
private uploadService: SectionUploadService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of available access condition
|
* The list of available access condition
|
||||||
@@ -113,10 +147,15 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
|||||||
@Input() submissionId: string;
|
@Input() submissionId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form model
|
* The list of all available metadata
|
||||||
* @type {DynamicFormControlModel[]}
|
|
||||||
*/
|
*/
|
||||||
public formModel: DynamicFormControlModel[];
|
@Input() formMetadata: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [JsonPatchOperationPathCombiner] object
|
||||||
|
* @type {JsonPatchOperationPathCombiner}
|
||||||
|
*/
|
||||||
|
@Input() pathCombiner: JsonPatchOperationPathCombiner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The FormComponent reference
|
* The FormComponent reference
|
||||||
@@ -124,29 +163,138 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
|||||||
@ViewChild('formRef') public formRef: FormComponent;
|
@ViewChild('formRef') public formRef: FormComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* The form model
|
||||||
*
|
* @type {DynamicFormControlModel[]}
|
||||||
* @param {ChangeDetectorRef} cdr
|
|
||||||
* @param {FormBuilderService} formBuilderService
|
|
||||||
* @param {FormService} formService
|
|
||||||
* @param {SubmissionService} submissionService
|
|
||||||
*/
|
*/
|
||||||
constructor(private cdr: ChangeDetectorRef,
|
formModel: DynamicFormControlModel[];
|
||||||
private formBuilderService: FormBuilderService,
|
|
||||||
private formService: FormService,
|
isSaving = false;
|
||||||
private submissionService: SubmissionService) {
|
|
||||||
|
protected subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
private static retrieveValueFromField(field: any) {
|
||||||
|
const temp = Array.isArray(field) ? field[0] : field;
|
||||||
|
return (temp) ? temp.value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Dispatch form model init
|
||||||
*/
|
*/
|
||||||
ngOnChanges() {
|
ngOnInit() {
|
||||||
if (this.fileData && this.formId) {
|
if (this.fileData && this.formId) {
|
||||||
this.formModel = this.buildFileEditForm();
|
this.formModel = this.buildFileEditForm();
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.unsubscribeAll();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize form model
|
* Initialize form model
|
||||||
*/
|
*/
|
||||||
@@ -229,98 +377,95 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize form model values
|
* Save bitstream metadata
|
||||||
*
|
|
||||||
* @param formModel
|
|
||||||
* The form model
|
|
||||||
*/
|
*/
|
||||||
public initModelData(formModel: DynamicFormControlModel[]) {
|
protected saveBitstreamData() {
|
||||||
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
// validate form
|
||||||
Array.of('name', 'startDate', 'endDate')
|
this.formService.validateAllFormFields(this.formRef.formGroup);
|
||||||
.filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key]))
|
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) => {
|
.forEach((key) => {
|
||||||
const metadataModel: any = this.formBuilderService.findById(key, formModel, index);
|
const metadataKey = key.replace(/_/g, '.');
|
||||||
if (metadataModel) {
|
const path = `metadata/${metadataKey}`;
|
||||||
if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) {
|
this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true);
|
||||||
const date = new Date(accessCondition[key]);
|
});
|
||||||
metadataModel.value = {
|
Object.keys((this.fileData.metadata))
|
||||||
year: date.getUTCFullYear(),
|
.filter((key) => isNotEmpty(this.fileData.metadata[key]))
|
||||||
month: date.getUTCMonth() + 1,
|
.filter((key) => hasNoValue(formData.metadata[key]))
|
||||||
day: date.getUTCDate()
|
.filter((key) => this.formMetadata.includes(key))
|
||||||
};
|
.forEach((key) => {
|
||||||
} else {
|
const metadataKey = key.replace(/_/g, '.');
|
||||||
metadataModel.value = accessCondition[key];
|
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 = SubmissionSectionUploadFileEditComponent.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 = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.startDate);
|
||||||
|
currentAccessCondition.startDate = dateToISOFormat(startDate);
|
||||||
}
|
}
|
||||||
|
if (!accessConditionOpt.hasEndDate) {
|
||||||
|
delete currentAccessCondition.endDate;
|
||||||
|
} else if (accessCondition.endDate) {
|
||||||
|
const endDate = SubmissionSectionUploadFileEditComponent.retrieveValueFromField(accessCondition.endDate);
|
||||||
|
currentAccessCondition.endDate = dateToISOFormat(endDate);
|
||||||
|
}
|
||||||
|
accessConditionsToSave.push(currentAccessCondition);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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.isSaving = false;
|
||||||
|
this.activeModal.close();
|
||||||
});
|
});
|
||||||
|
this.subscriptions.push(saveBitstreamDataSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private unsubscribeAll() {
|
||||||
* Dispatch form model update when changing an access condition
|
this.subscriptions.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
*
|
|
||||||
* @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 (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()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,14 +9,14 @@
|
|||||||
<h3>{{fileName}} <span class="text-muted">({{fileData?.sizeBytes | dsFileSize}})</span></h3>
|
<h3>{{fileName}} <span class="text-muted">({{fileData?.sizeBytes | dsFileSize}})</span></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="float-right w-15" [class.sticky-buttons]="!readMode">
|
<div class="float-right w-15" [class.sticky-buttons]="!readMode">
|
||||||
<ng-container *ngIf="readMode">
|
<ng-container>
|
||||||
<ds-file-download-link [cssClasses]="'btn btn-link-focus'" [isBlank]="true" [bitstream]="getBitstream()" [enableRequestACopy]="false">
|
<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>
|
<i class="fa fa-download fa-2x text-normal" aria-hidden="true"></i>
|
||||||
</ds-file-download-link>
|
</ds-file-download-link>
|
||||||
<button class="btn btn-link-focus"
|
<button class="btn btn-link-focus"
|
||||||
[attr.aria-label]="'submission.sections.upload.edit.title' | translate"
|
[attr.aria-label]="'submission.sections.upload.edit.title' | translate"
|
||||||
title="{{ '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>
|
<i class="fa fa-edit fa-2x text-normal"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link-focus"
|
<button class="btn btn-link-focus"
|
||||||
@@ -28,40 +28,9 @@
|
|||||||
<i *ngIf="!(processingDelete$ | async)" class="fa fa-trash fa-2x text-danger"></i>
|
<i *ngIf="!(processingDelete$ | async)" class="fa fa-trash fa-2x text-danger"></i>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</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>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<ds-submission-section-upload-file-view *ngIf="readMode"
|
<ds-submission-section-upload-file-view [fileData]="fileData"></ds-submission-section-upload-file-view>
|
||||||
[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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -1,25 +1,23 @@
|
|||||||
import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
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 { DynamicFormControlModel, } from '@ng-dynamic-forms/core';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
import { SectionUploadService } from '../section-upload.service';
|
import { SectionUploadService } from '../section-upload.service';
|
||||||
import { hasNoValue, isNotEmpty, isNotNull, isNotUndefined } from '../../../../shared/empty.util';
|
import { hasValue, isNotUndefined } from '../../../../shared/empty.util';
|
||||||
import { FormService } from '../../../../shared/form/form.service';
|
import { FormService } from '../../../../shared/form/form.service';
|
||||||
import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder';
|
import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder';
|
||||||
import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
import { JsonPatchOperationPathCombiner } from '../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||||
import { WorkspaceitemSectionUploadFileObject } from '../../../../core/submission/models/workspaceitem-section-upload-file.model';
|
import { WorkspaceitemSectionUploadFileObject } from '../../../../core/submission/models/workspaceitem-section-upload-file.model';
|
||||||
import { SubmissionFormsModel } from '../../../../core/config/models/config-submission-forms.model';
|
import { SubmissionFormsModel } from '../../../../core/config/models/config-submission-forms.model';
|
||||||
import { dateToISOFormat } from '../../../../shared/date.util';
|
|
||||||
import { SubmissionService } from '../../../submission.service';
|
import { SubmissionService } from '../../../submission.service';
|
||||||
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||||
import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.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 { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
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
|
* This component represents a single bitstream contained in the submission
|
||||||
@@ -87,6 +85,13 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() submissionId: string;
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [[SubmissionSectionUploadFileEditComponent]] reference
|
||||||
|
* @type {SubmissionSectionUploadFileEditComponent}
|
||||||
|
*/
|
||||||
|
@ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bitstream's metadata data
|
* The bitstream's metadata data
|
||||||
* @type {WorkspaceitemSectionUploadFileObject}
|
* @type {WorkspaceitemSectionUploadFileObject}
|
||||||
@@ -135,12 +140,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
*/
|
*/
|
||||||
protected formMetadata: string[] = [];
|
protected formMetadata: string[] = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* The [[SubmissionSectionUploadFileEditComponent]] reference
|
|
||||||
* @type {SubmissionSectionUploadFileEditComponent}
|
|
||||||
*/
|
|
||||||
@ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
*
|
*
|
||||||
@@ -153,28 +152,19 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
* @param {SubmissionService} submissionService
|
* @param {SubmissionService} submissionService
|
||||||
* @param {SectionUploadService} uploadService
|
* @param {SectionUploadService} uploadService
|
||||||
*/
|
*/
|
||||||
constructor(private cdr: ChangeDetectorRef,
|
constructor(
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
private formService: FormService,
|
private formService: FormService,
|
||||||
private halService: HALEndpointService,
|
private halService: HALEndpointService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||||
private operationsService: SubmissionJsonPatchOperationsService,
|
private operationsService: SubmissionJsonPatchOperationsService,
|
||||||
private submissionService: SubmissionService,
|
private submissionService: SubmissionService,
|
||||||
private uploadService: SectionUploadService) {
|
private uploadService: SectionUploadService,
|
||||||
|
) {
|
||||||
this.readMode = true;
|
this.readMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected loadFormMetadata() {
|
|
||||||
this.configMetadataForm.rows.forEach((row) => {
|
|
||||||
row.fields.forEach((field) => {
|
|
||||||
field.selectableMetadata.forEach((metadatum) => {
|
|
||||||
this.formMetadata.push(metadatum.metadata);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve bitstream's metadata
|
* Retrieve bitstream's metadata
|
||||||
*/
|
*/
|
||||||
@@ -202,22 +192,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
this.loadFormMetadata();
|
this.loadFormMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show confirmation dialog for delete
|
* Show confirmation dialog for delete
|
||||||
*/
|
*/
|
||||||
@@ -243,108 +217,6 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Save bitstream metadata
|
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
* the click event emitted
|
|
||||||
*/
|
|
||||||
public saveBitstreamData(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
private retrieveValueFromField(field: any) {
|
|
||||||
const temp = Array.isArray(field) ? field[0] : field;
|
|
||||||
return (temp) ? temp.value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch from edit form to metadata view
|
* Switch from edit form to metadata view
|
||||||
*/
|
*/
|
||||||
@@ -353,4 +225,67 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
|||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editBitstreamData() {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*activeModal.componentInstance.saveBitstreamDataEvent.subscribe((res) => {
|
||||||
|
console.log(JSON.stringify(res));
|
||||||
|
});*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user