mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
[CST-12043] feature: add primary bitstream switch
This commit is contained in:
@@ -4,7 +4,10 @@ import { WorkspaceitemSectionUploadFileObject } from './workspaceitem-section-up
|
|||||||
* An interface to represent submission's upload section data.
|
* An interface to represent submission's upload section data.
|
||||||
*/
|
*/
|
||||||
export interface WorkspaceitemSectionUploadObject {
|
export interface WorkspaceitemSectionUploadObject {
|
||||||
|
/**
|
||||||
|
* Primary bitstream flag
|
||||||
|
*/
|
||||||
|
primary: string | null;
|
||||||
/**
|
/**
|
||||||
* A list of [[WorkspaceitemSectionUploadFileObject]]
|
* A list of [[WorkspaceitemSectionUploadFileObject]]
|
||||||
*/
|
*/
|
||||||
|
@@ -59,6 +59,7 @@ export const SubmissionObjectActionTypes = {
|
|||||||
// Upload file types
|
// Upload file types
|
||||||
NEW_FILE: type('dspace/submission/NEW_FILE'),
|
NEW_FILE: type('dspace/submission/NEW_FILE'),
|
||||||
EDIT_FILE_DATA: type('dspace/submission/EDIT_FILE_DATA'),
|
EDIT_FILE_DATA: type('dspace/submission/EDIT_FILE_DATA'),
|
||||||
|
EDIT_FILE_PRIMARY_BITSTREAM_DATA: type('dspace/submission/EDIT_FILE_PRIMARY_BITSTREAM_DATA'),
|
||||||
DELETE_FILE: type('dspace/submission/DELETE_FILE'),
|
DELETE_FILE: type('dspace/submission/DELETE_FILE'),
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
@@ -760,6 +761,29 @@ export class NewUploadedFileAction implements Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EditFilePrimaryBitstreamAction implements Action {
|
||||||
|
type = SubmissionObjectActionTypes.EDIT_FILE_PRIMARY_BITSTREAM_DATA;
|
||||||
|
payload: {
|
||||||
|
submissionId: string;
|
||||||
|
sectionId: string;
|
||||||
|
fileId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a file data
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* the submission's ID
|
||||||
|
* @param sectionId
|
||||||
|
* the section's ID
|
||||||
|
* @param fileId
|
||||||
|
* the file's ID
|
||||||
|
*/
|
||||||
|
constructor(submissionId: string, sectionId: string, fileId: string) {
|
||||||
|
this.payload = { submissionId, sectionId, fileId: fileId };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class EditFileDataAction implements Action {
|
export class EditFileDataAction implements Action {
|
||||||
type = SubmissionObjectActionTypes.EDIT_FILE_DATA;
|
type = SubmissionObjectActionTypes.EDIT_FILE_DATA;
|
||||||
payload: {
|
payload: {
|
||||||
@@ -833,6 +857,7 @@ export type SubmissionObjectAction = DisableSectionAction
|
|||||||
| SectionStatusChangeAction
|
| SectionStatusChangeAction
|
||||||
| NewUploadedFileAction
|
| NewUploadedFileAction
|
||||||
| EditFileDataAction
|
| EditFileDataAction
|
||||||
|
| EditFilePrimaryBitstreamAction
|
||||||
| DeleteUploadedFileAction
|
| DeleteUploadedFileAction
|
||||||
| InertSectionErrorsAction
|
| InertSectionErrorsAction
|
||||||
| DeleteSectionErrorsAction
|
| DeleteSectionErrorsAction
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { hasValue, isEmpty, isNotEmpty, isNotNull, isUndefined } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty, isNotNull, isNull, isUndefined } from '../../shared/empty.util';
|
||||||
import differenceWith from 'lodash/differenceWith';
|
import differenceWith from 'lodash/differenceWith';
|
||||||
import findKey from 'lodash/findKey';
|
import findKey from 'lodash/findKey';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
DepositSubmissionSuccessAction,
|
DepositSubmissionSuccessAction,
|
||||||
DisableSectionAction,
|
DisableSectionAction,
|
||||||
EditFileDataAction,
|
EditFileDataAction,
|
||||||
|
EditFilePrimaryBitstreamAction,
|
||||||
EnableSectionAction,
|
EnableSectionAction,
|
||||||
InertSectionErrorsAction,
|
InertSectionErrorsAction,
|
||||||
InitSectionAction,
|
InitSectionAction,
|
||||||
@@ -203,6 +204,10 @@ export function submissionObjectReducer(state = initialState, action: Submission
|
|||||||
return newFile(state, action as NewUploadedFileAction);
|
return newFile(state, action as NewUploadedFileAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SubmissionObjectActionTypes.EDIT_FILE_PRIMARY_BITSTREAM_DATA: {
|
||||||
|
return editPrimaryBitstream(state, action as EditFilePrimaryBitstreamAction);
|
||||||
|
}
|
||||||
|
|
||||||
case SubmissionObjectActionTypes.EDIT_FILE_DATA: {
|
case SubmissionObjectActionTypes.EDIT_FILE_DATA: {
|
||||||
return editFileData(state, action as EditFileDataAction);
|
return editFileData(state, action as EditFileDataAction);
|
||||||
}
|
}
|
||||||
@@ -735,6 +740,46 @@ function newFile(state: SubmissionObjectState, action: NewUploadedFileAction): S
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit primary bitstream.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* the current state
|
||||||
|
* @param action
|
||||||
|
* an EditFilePrimaryBitstreamAction action
|
||||||
|
* @return SubmissionObjectState
|
||||||
|
* the new state, with the edited file.
|
||||||
|
*/
|
||||||
|
function editPrimaryBitstream(state: SubmissionObjectState, action: EditFilePrimaryBitstreamAction): SubmissionObjectState {
|
||||||
|
const filesData = state[ action.payload.submissionId ].sections[ action.payload.sectionId ].data as WorkspaceitemSectionUploadObject;
|
||||||
|
const { submissionId, sectionId, fileId } = action.payload;
|
||||||
|
|
||||||
|
const fileIndex = findKey(filesData.files, { uuid: fileId });
|
||||||
|
if (isNull(fileIndex)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submission = state[submissionId];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[submissionId]: {
|
||||||
|
...submission,
|
||||||
|
sections: {
|
||||||
|
...submission.sections,
|
||||||
|
[sectionId]: {
|
||||||
|
...submission.sections[sectionId],
|
||||||
|
data: {
|
||||||
|
...submission.sections[sectionId].data as WorkspaceitemSectionUploadObject,
|
||||||
|
primary: fileId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isLoading: submission.isLoading,
|
||||||
|
savePending: submission.savePending,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a file.
|
* Edit a file.
|
||||||
*
|
*
|
||||||
|
@@ -28,6 +28,8 @@ import {
|
|||||||
BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT,
|
BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT,
|
||||||
BITSTREAM_FORM_ACCESS_CONDITION_TYPE_CONFIG,
|
BITSTREAM_FORM_ACCESS_CONDITION_TYPE_CONFIG,
|
||||||
BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT,
|
BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT,
|
||||||
|
BITSTREAM_FORM_PRIMARY,
|
||||||
|
BITSTREAM_FORM_PRIMARY_LAYOUT,
|
||||||
BITSTREAM_METADATA_FORM_GROUP_CONFIG,
|
BITSTREAM_METADATA_FORM_GROUP_CONFIG,
|
||||||
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
||||||
} from './section-upload-file-edit.model';
|
} from './section-upload-file-edit.model';
|
||||||
@@ -40,12 +42,10 @@ 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 { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { filter, mergeMap, take } from 'rxjs/operators';
|
import { filter, mergeMap, take, tap } from 'rxjs/operators';
|
||||||
import { dateToISOFormat } from '../../../../../shared/date.util';
|
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 { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder';
|
||||||
import {
|
import {
|
||||||
SubmissionJsonPatchOperationsService
|
SubmissionJsonPatchOperationsService
|
||||||
@@ -54,9 +54,10 @@ import {
|
|||||||
JsonPatchOperationPathCombiner
|
JsonPatchOperationPathCombiner
|
||||||
} from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
} from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||||
import { SectionUploadService } from '../../section-upload.service';
|
import { SectionUploadService } from '../../section-upload.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { Observable, Subject, Subscription } from 'rxjs';
|
||||||
import { DynamicFormControlCondition } from '@ng-dynamic-forms/core/lib/model/misc/dynamic-form-control-relation.model';
|
import { DynamicFormControlCondition } from '@ng-dynamic-forms/core/lib/model/misc/dynamic-form-control-relation.model';
|
||||||
import { DynamicDateControlValue } from '@ng-dynamic-forms/core/lib/model/dynamic-date-control.model';
|
import { DynamicDateControlValue } from '@ng-dynamic-forms/core/lib/model/dynamic-date-control.model';
|
||||||
|
import { DynamicCustomSwitchModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component represents the edit form for bitstream
|
* This component represents the edit form for bitstream
|
||||||
@@ -74,6 +75,13 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
*/
|
*/
|
||||||
@ViewChild('formRef') public formRef: FormComponent;
|
@ViewChild('formRef') public formRef: FormComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The indicator is the primary bitstream
|
||||||
|
* it will be null if no primary bitstream is set for the ORIGINAL bundle
|
||||||
|
* @type {boolean, null}
|
||||||
|
*/
|
||||||
|
isPrimary: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of available access condition
|
* The list of available access condition
|
||||||
* @type {Array}
|
* @type {Array}
|
||||||
@@ -160,6 +168,12 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
|
|
||||||
protected subscriptions: Subscription[] = [];
|
protected subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
private onSaveBitstreamData$: Subject<any> = new Subject();
|
||||||
|
|
||||||
|
get saveBitstreamDataEvent$(): Observable<void> {
|
||||||
|
return this.onSaveBitstreamData$.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
*
|
*
|
||||||
@@ -191,6 +205,10 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
* The form model
|
* The form model
|
||||||
*/
|
*/
|
||||||
public initModelData(formModel: DynamicFormControlModel[]) {
|
public initModelData(formModel: DynamicFormControlModel[]) {
|
||||||
|
|
||||||
|
const primaryBitstreammodel: any = this.formBuilderService.findById('primaryBitstream', formModel, this.fileIndex);
|
||||||
|
primaryBitstreammodel.value = this.isPrimary || false;
|
||||||
|
|
||||||
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
||||||
Array.of('name', 'startDate', 'endDate')
|
Array.of('name', 'startDate', 'endDate')
|
||||||
.filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key]))
|
.filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key]))
|
||||||
@@ -291,6 +309,9 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
const formModel: DynamicFormControlModel[] = [];
|
const formModel: DynamicFormControlModel[] = [];
|
||||||
|
|
||||||
|
formModel.push(new DynamicCustomSwitchModel(BITSTREAM_FORM_PRIMARY, BITSTREAM_METADATA_FORM_GROUP_LAYOUT));
|
||||||
|
|
||||||
const metadataGroupModelConfig = Object.assign({}, BITSTREAM_METADATA_FORM_GROUP_CONFIG);
|
const metadataGroupModelConfig = Object.assign({}, BITSTREAM_METADATA_FORM_GROUP_CONFIG);
|
||||||
metadataGroupModelConfig.group = this.formBuilderService.modelFromConfiguration(
|
metadataGroupModelConfig.group = this.formBuilderService.modelFromConfiguration(
|
||||||
this.submissionId,
|
this.submissionId,
|
||||||
@@ -386,25 +407,29 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
return formModel;
|
return formModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save bitstream metadata
|
* Save bitstream metadata
|
||||||
*/
|
*/
|
||||||
saveBitstreamData() {
|
saveBitstreamData() {
|
||||||
|
|
||||||
|
const pathFragment = ['files', this.fileIndex];
|
||||||
|
|
||||||
// validate form
|
// validate form
|
||||||
this.formService.validateAllFormFields(this.formRef.formGroup);
|
this.formService.validateAllFormFields(this.formRef.formGroup);
|
||||||
const saveBitstreamDataSubscription = this.formService.isValid(this.formId).pipe(
|
const subscription = this.formService.isValid(this.formId).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
filter((isValid) => isValid),
|
filter((isValid) => isValid),
|
||||||
mergeMap(() => this.formService.getFormData(this.formId)),
|
mergeMap(() => this.formService.getFormData(this.formId)),
|
||||||
take(1),
|
take(1),
|
||||||
mergeMap((formData: any) => {
|
tap((formData: any) => {
|
||||||
// collect bitstream metadata
|
// collect bitstream metadata
|
||||||
Object.keys((formData.metadata))
|
Object.keys((formData.metadata))
|
||||||
.filter((key) => isNotEmpty(formData.metadata[key]))
|
.filter((key) => isNotEmpty(formData.metadata[key]))
|
||||||
.forEach((key) => {
|
.forEach((key) => {
|
||||||
const metadataKey = key.replace(/_/g, '.');
|
const metadataKey = key.replace(/_/g, '.');
|
||||||
const path = `metadata/${metadataKey}`;
|
const path = `metadata/${metadataKey}`;
|
||||||
this.operationsBuilder.add(this.pathCombiner.getPath(path), formData.metadata[key], true);
|
this.operationsBuilder.add(this.pathCombiner.getPath([...pathFragment, path]), formData.metadata[key], true);
|
||||||
});
|
});
|
||||||
Object.keys((this.fileData.metadata))
|
Object.keys((this.fileData.metadata))
|
||||||
.filter((key) => isNotEmpty(this.fileData.metadata[key]))
|
.filter((key) => isNotEmpty(this.fileData.metadata[key]))
|
||||||
@@ -413,7 +438,7 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
.forEach((key) => {
|
.forEach((key) => {
|
||||||
const metadataKey = key.replace(/_/g, '.');
|
const metadataKey = key.replace(/_/g, '.');
|
||||||
const path = `metadata/${metadataKey}`;
|
const path = `metadata/${metadataKey}`;
|
||||||
this.operationsBuilder.remove(this.pathCombiner.getPath(path));
|
this.operationsBuilder.remove(this.pathCombiner.getPath([...pathFragment, path]));
|
||||||
});
|
});
|
||||||
const accessConditionsToSave = [];
|
const accessConditionsToSave = [];
|
||||||
formData.accessConditions
|
formData.accessConditions
|
||||||
@@ -467,29 +492,11 @@ export class SubmissionSectionUploadFileEditComponent
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (isNotEmpty(accessConditionsToSave)) {
|
if (isNotEmpty(accessConditionsToSave)) {
|
||||||
this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true);
|
this.operationsBuilder.add(this.pathCombiner.getPath([...pathFragment, '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[]) => {
|
).subscribe((formData) => this.onSaveBitstreamData$.next(formData));
|
||||||
if (result[0].sections[this.sectionId]) {
|
this.subscriptions.push(subscription);
|
||||||
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() {
|
private unsubscribeAll() {
|
||||||
|
@@ -4,6 +4,7 @@ import {
|
|||||||
DynamicFormControlLayout,
|
DynamicFormControlLayout,
|
||||||
DynamicFormGroupModelConfig,
|
DynamicFormGroupModelConfig,
|
||||||
DynamicSelectModelConfig,
|
DynamicSelectModelConfig,
|
||||||
|
DynamicSwitchModelConfig,
|
||||||
MATCH_ENABLED,
|
MATCH_ENABLED,
|
||||||
OR_OPERATOR,
|
OR_OPERATOR,
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
@@ -56,6 +57,18 @@ export const BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT: DynamicFormControlLayo
|
|||||||
label: 'col-form-label name-label'
|
label: 'col-form-label name-label'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
export const BITSTREAM_FORM_PRIMARY_LAYOUT: DynamicFormControlLayout = {
|
||||||
|
grid: {
|
||||||
|
host: 'col-6'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BITSTREAM_FORM_PRIMARY: DynamicSwitchModelConfig = {
|
||||||
|
id: 'primaryBitstream',
|
||||||
|
name: 'primaryBitstream',
|
||||||
|
label: 'Primary bitstream',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG: DynamicDatePickerModelConfig = {
|
export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG: DynamicDatePickerModelConfig = {
|
||||||
id: 'startDate',
|
id: 'startDate',
|
||||||
|
@@ -1,6 +1,13 @@
|
|||||||
<ng-container *ngIf="fileData">
|
<ng-container *ngIf="fileData">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<!-- Default switch -->
|
||||||
|
<div class="col-md-2 d-flex justify-content-center align-items-center" >
|
||||||
|
<div class="custom-control custom-switch">
|
||||||
|
<input type="checkbox" class="custom-control-input" id="primaryBitstream{{fileIndex}}" [(ngModel)]="isPrimary" (change)="togglePrimaryBitstream()">
|
||||||
|
<label class="custom-control-label" for="primaryBitstream{{fileIndex}}"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
<div class="float-left w-75">
|
<div class="float-left w-75">
|
||||||
<h3>{{fileName}} <span class="text-muted">({{fileData?.sizeBytes | dsFileSize}})</span></h3>
|
<h3>{{fileName}} <span class="text-muted">({{fileData?.sizeBytes | dsFileSize}})</span></h3>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter, switchMap, tap } 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';
|
||||||
|
|
||||||
@@ -27,6 +27,9 @@ import { SubmissionJsonPatchOperationsService } from '../../../../core/submissio
|
|||||||
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';
|
import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config';
|
||||||
|
import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model';
|
||||||
|
import { SubmissionObject } from 'src/app/core/submission/models/submission-object.model';
|
||||||
|
import { NotificationsService } from 'src/app/shared/notifications/notifications.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component represents a single bitstream contained in the submission
|
* This component represents a single bitstream contained in the submission
|
||||||
@@ -37,6 +40,19 @@ import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap/modal/modal-config';
|
|||||||
templateUrl: './section-upload-file.component.html',
|
templateUrl: './section-upload-file.component.html',
|
||||||
})
|
})
|
||||||
export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, OnDestroy {
|
export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* The indicator is the primary bitstream
|
||||||
|
* it will be null if no primary bitstream is set for the ORIGINAL bundle
|
||||||
|
* @type {boolean, null}
|
||||||
|
*/
|
||||||
|
@Input() set isPrimaryBitstream(status: boolean | null) {
|
||||||
|
this.initialPrimaryStatus = status;
|
||||||
|
this.isPrimary = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private initialPrimaryStatus = false;
|
||||||
|
isPrimary = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of available access condition
|
* The list of available access condition
|
||||||
@@ -137,6 +153,12 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
|
|||||||
*/
|
*/
|
||||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [JsonPatchOperationPathCombiner] object
|
||||||
|
* @type {JsonPatchOperationPathCombiner}
|
||||||
|
*/
|
||||||
|
protected primaryBitstreamPathCombiner: JsonPatchOperationPathCombiner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array to track all subscriptions and unsubscribe them onDestroy
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
* @type {Array}
|
* @type {Array}
|
||||||
@@ -165,6 +187,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
|
|||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
private formService: FormService,
|
private formService: FormService,
|
||||||
private halService: HALEndpointService,
|
private halService: HALEndpointService,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||||
private operationsService: SubmissionJsonPatchOperationsService,
|
private operationsService: SubmissionJsonPatchOperationsService,
|
||||||
@@ -197,7 +220,7 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
|
|||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.formId = this.formService.getUniqueId(this.fileId);
|
this.formId = this.formService.getUniqueId(this.fileId);
|
||||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId, 'files', this.fileIndex);
|
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionId);
|
||||||
this.loadFormMetadata();
|
this.loadFormMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +249,23 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updatePrimaryBitstream(primaryBitstream: boolean): void {
|
||||||
|
const path = this.pathCombiner.getPath('primary');
|
||||||
|
|
||||||
|
if (this.isPrimary === null && primaryBitstream) {
|
||||||
|
this.operationsBuilder.add(path, this.fileId, false, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isPrimary !== primaryBitstream) {
|
||||||
|
if (primaryBitstream) {
|
||||||
|
this.operationsBuilder.replace(path, this.fileId, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.operationsBuilder.remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
editBitstreamData() {
|
editBitstreamData() {
|
||||||
|
|
||||||
const options: NgbModalOptions = {
|
const options: NgbModalOptions = {
|
||||||
@@ -247,7 +287,52 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
|
|||||||
activeModal.componentInstance.formMetadata = this.formMetadata;
|
activeModal.componentInstance.formMetadata = this.formMetadata;
|
||||||
activeModal.componentInstance.pathCombiner = this.pathCombiner;
|
activeModal.componentInstance.pathCombiner = this.pathCombiner;
|
||||||
activeModal.componentInstance.submissionId = this.submissionId;
|
activeModal.componentInstance.submissionId = this.submissionId;
|
||||||
|
activeModal.componentInstance.isPrimary = this.isPrimary;
|
||||||
|
|
||||||
|
activeModal.componentInstance.saveBitstreamDataEvent$.pipe(
|
||||||
|
// TODO: uncoment
|
||||||
|
tap((data: any) => {
|
||||||
|
this.updatePrimaryBitstream(data.primaryBitstream[0]);
|
||||||
|
}),
|
||||||
|
switchMap(() => this.patchFileOperations())
|
||||||
|
).subscribe((result: SubmissionObject[]) => {
|
||||||
|
this.uploadFileData(result);
|
||||||
|
activeModal.componentInstance.isSaving = false;
|
||||||
|
activeModal.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private uploadFileData(result: SubmissionObject[]) {
|
||||||
|
const section = result[0].sections[this.sectionId];
|
||||||
|
if (!section) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let uploadSection = (section as WorkspaceitemSectionUploadObject);
|
||||||
|
uploadSection = {...uploadSection, primary: this.fileId};
|
||||||
|
|
||||||
|
// TODO: update only if primary bitstream changed
|
||||||
|
// TODO: if the result contains primary id of this file of null then update
|
||||||
|
this.uploadService.updateFilePrimaryBitstream(this.submissionId, this.sectionId, this.fileId);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private patchFileOperations() {
|
||||||
|
return this.operationsService.jsonPatchByResourceID(
|
||||||
|
this.submissionService.getSubmissionObjectLinkName(),
|
||||||
|
this.submissionId,
|
||||||
|
this.pathCombiner.rootElement,
|
||||||
|
this.pathCombiner.subRootElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePrimaryBitstream() {
|
||||||
|
this.updatePrimaryBitstream(!this.isPrimary);
|
||||||
|
this.submissionService.dispatchSaveSection(this.submissionId, this.sectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
@@ -273,7 +358,13 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit,
|
|||||||
* Delete bitstream from submission
|
* Delete bitstream from submission
|
||||||
*/
|
*/
|
||||||
protected deleteFile() {
|
protected deleteFile() {
|
||||||
this.operationsBuilder.remove(this.pathCombiner.getPath());
|
this.operationsBuilder.remove(this.pathCombiner.getPath(['files', this.fileIndex]));
|
||||||
|
|
||||||
|
if (this.isPrimary) {
|
||||||
|
// TODO: uncoment
|
||||||
|
// this.operationsBuilder.remove(this.pathCombiner.getPath('primary'));
|
||||||
|
}
|
||||||
|
|
||||||
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
|
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
|
||||||
this.submissionService.getSubmissionObjectLinkName(),
|
this.submissionService.getSubmissionObjectLinkName(),
|
||||||
this.submissionId,
|
this.submissionId,
|
||||||
|
@@ -17,6 +17,13 @@ export class ThemedSubmissionSectionUploadFileComponent
|
|||||||
*/
|
*/
|
||||||
@Input() availableAccessConditionOptions: any[];
|
@Input() availableAccessConditionOptions: any[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The indicator is the primary bitstream
|
||||||
|
* it will be null if no primary bitstream is set for the ORIGINAL bundle
|
||||||
|
* @type {boolean, null}
|
||||||
|
*/
|
||||||
|
@Input() isPrimary: boolean | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The submission id
|
* The submission id
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -69,6 +76,7 @@ export class ThemedSubmissionSectionUploadFileComponent
|
|||||||
|
|
||||||
protected inAndOutputNames: (keyof SubmissionSectionUploadFileComponent & keyof this)[] = [
|
protected inAndOutputNames: (keyof SubmissionSectionUploadFileComponent & keyof this)[] = [
|
||||||
'availableAccessConditionOptions',
|
'availableAccessConditionOptions',
|
||||||
|
'isPrimary',
|
||||||
'collectionId',
|
'collectionId',
|
||||||
'collectionPolicyType',
|
'collectionPolicyType',
|
||||||
'configMetadataForm',
|
'configMetadataForm',
|
||||||
|
@@ -2,15 +2,7 @@
|
|||||||
[dismissible]="true"
|
[dismissible]="true"
|
||||||
[type]="AlertTypeEnum.Info"></ds-alert>
|
[type]="AlertTypeEnum.Info"></ds-alert>
|
||||||
|
|
||||||
<ng-container *ngIf="fileList.length == 0">
|
<ng-container *ngIf="fileList.length > 0; else noFileUploaded">
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<h3 class="text-center"><span class="text-muted">{{'submission.sections.upload.no-file-uploaded' | translate}}</span></h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngIf="fileList.length > 0">
|
|
||||||
|
|
||||||
<div *ngIf="collectionDefaultAccessConditions.length > 0" class="row">
|
<div *ngIf="collectionDefaultAccessConditions.length > 0" class="row">
|
||||||
<div class="col-sm-12" >
|
<div class="col-sm-12" >
|
||||||
@@ -26,16 +18,16 @@
|
|||||||
</ds-alert>
|
</ds-alert>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-container *ngFor="let fileEntry of fileList; let i = index;">
|
||||||
<ng-container *ngFor="let fileEntry of fileList">
|
|
||||||
<ds-themed-submission-upload-section-file
|
<ds-themed-submission-upload-section-file
|
||||||
|
[isPrimary]="primaryBitstreamUUID ? primaryBitstreamUUID === fileEntry.uuid : null"
|
||||||
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
||||||
[collectionId]="collectionId"
|
[collectionId]="collectionId"
|
||||||
[collectionPolicyType]="collectionPolicyType"
|
[collectionPolicyType]="collectionPolicyType"
|
||||||
[configMetadataForm]="(configMetadataForm$ | async)"
|
[configMetadataForm]="(configMetadataForm$ | async)"
|
||||||
[fileId]="fileIndexes[fileList.indexOf(fileEntry)]"
|
[fileId]="fileEntry.uuid"
|
||||||
[fileIndex]="fileList.indexOf(fileEntry)"
|
[fileIndex]="i"
|
||||||
[fileName]="fileNames[fileList.indexOf(fileEntry)]"
|
[fileName]="fileNames[i]"
|
||||||
[sectionId]="sectionData.id"
|
[sectionId]="sectionData.id"
|
||||||
[submissionId]="submissionId"></ds-themed-submission-upload-section-file>
|
[submissionId]="submissionId"></ds-themed-submission-upload-section-file>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -45,3 +37,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template #noFileUploaded>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h3 class="text-center"><span class="text-muted">{{'submission.sections.upload.no-file-uploaded' | translate}}</span></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
@@ -4,7 +4,8 @@ import {
|
|||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
combineLatest as observableCombineLatest,
|
combineLatest as observableCombineLatest,
|
||||||
Observable,
|
Observable,
|
||||||
Subscription
|
Subscription,
|
||||||
|
combineLatest
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ import { AccessConditionOption } from '../../../core/config/models/config-access
|
|||||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { getFirstSucceededRemoteData } from '../../../core/shared/operators';
|
import { getFirstSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model';
|
||||||
|
|
||||||
export const POLICY_DEFAULT_NO_LIST = 1; // Banner1
|
export const POLICY_DEFAULT_NO_LIST = 1; // Banner1
|
||||||
export const POLICY_DEFAULT_WITH_LIST = 2; // Banner2
|
export const POLICY_DEFAULT_WITH_LIST = 2; // Banner2
|
||||||
@@ -58,10 +60,10 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
|||||||
public AlertTypeEnum = AlertType;
|
public AlertTypeEnum = AlertType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The array containing the keys of file list array
|
* The uuid of primary bitstream file
|
||||||
* @type {Array}
|
* @type {Array}
|
||||||
*/
|
*/
|
||||||
public fileIndexes: string[] = [];
|
public primaryBitstreamUUID: string | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The file list
|
* The file list
|
||||||
@@ -194,27 +196,18 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
|||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
// retrieve submission's bitstreams from state
|
// retrieve submission's bitstreams from state
|
||||||
observableCombineLatest(this.configMetadataForm$,
|
combineLatest([this.configMetadataForm$,
|
||||||
this.bitstreamService.getUploadedFileList(this.submissionId, this.sectionData.id)).pipe(
|
this.bitstreamService.getUploadedFilesData(this.submissionId, this.sectionData.id)]).pipe(
|
||||||
filter(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
|
filter(([configMetadataForm, { files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => {
|
||||||
return isNotEmpty(configMetadataForm) && isNotUndefined(fileList);
|
return isNotEmpty(configMetadataForm) && isNotEmpty(files);
|
||||||
}),
|
}),
|
||||||
distinctUntilChanged())
|
distinctUntilChanged())
|
||||||
.subscribe(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
|
.subscribe(([configMetadataForm, { primary, files }]: [SubmissionFormsModel, WorkspaceitemSectionUploadObject]) => {
|
||||||
this.fileList = [];
|
this.primaryBitstreamUUID = primary;
|
||||||
this.fileIndexes = [];
|
this.fileList = files;
|
||||||
this.fileNames = [];
|
this.fileNames = Array.from(files, file => this.getFileName(configMetadataForm, file));
|
||||||
this.changeDetectorRef.detectChanges();
|
|
||||||
if (isNotUndefined(fileList) && fileList.length > 0) {
|
|
||||||
fileList.forEach((file) => {
|
|
||||||
this.fileList.push(file);
|
|
||||||
this.fileIndexes.push(file.uuid);
|
|
||||||
this.fileNames.push(this.getFileName(configMetadataForm, file));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeDetectorRef.detectChanges();
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -8,11 +8,13 @@ import { SubmissionState } from '../../submission.reducers';
|
|||||||
import {
|
import {
|
||||||
DeleteUploadedFileAction,
|
DeleteUploadedFileAction,
|
||||||
EditFileDataAction,
|
EditFileDataAction,
|
||||||
|
EditFilePrimaryBitstreamAction,
|
||||||
NewUploadedFileAction
|
NewUploadedFileAction
|
||||||
} from '../../objects/submission-objects.actions';
|
} from '../../objects/submission-objects.actions';
|
||||||
import { submissionUploadedFileFromUuidSelector, submissionUploadedFilesFromIdSelector } from '../../selectors';
|
import { submissionSectionDataFromIdSelector, submissionUploadedFileFromUuidSelector, submissionUploadedFilesFromIdSelector } from '../../selectors';
|
||||||
import { isUndefined } from '../../../shared/empty.util';
|
import { isUndefined } from '../../../shared/empty.util';
|
||||||
import { WorkspaceitemSectionUploadFileObject } from '../../../core/submission/models/workspaceitem-section-upload-file.model';
|
import { WorkspaceitemSectionUploadFileObject } from '../../../core/submission/models/workspaceitem-section-upload-file.model';
|
||||||
|
import { WorkspaceitemSectionUploadObject } from 'src/app/core/submission/models/workspaceitem-section-upload.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service that provides methods to handle submission's bitstream state.
|
* A service that provides methods to handle submission's bitstream state.
|
||||||
@@ -27,6 +29,22 @@ export class SectionUploadService {
|
|||||||
*/
|
*/
|
||||||
constructor(private store: Store<SubmissionState>) {}
|
constructor(private store: Store<SubmissionState>) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return submission's bitstream data from state
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @returns {WorkspaceitemSectionUploadObject}
|
||||||
|
* Returns submission's bitstream data
|
||||||
|
*/
|
||||||
|
public getUploadedFilesData(submissionId: string, sectionId: string): Observable<WorkspaceitemSectionUploadObject> {
|
||||||
|
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
|
map((state) => state),
|
||||||
|
distinctUntilChanged());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return submission's bitstream list from state
|
* Return submission's bitstream list from state
|
||||||
*
|
*
|
||||||
@@ -104,6 +122,22 @@ export class SectionUploadService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update primary bitstream into the state
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @param fileUUID
|
||||||
|
* The bitstream UUID
|
||||||
|
*/
|
||||||
|
public updateFilePrimaryBitstream(submissionId: string, sectionId: string, fileUUID: string) {
|
||||||
|
this.store.dispatch(
|
||||||
|
new EditFilePrimaryBitstreamAction(submissionId, sectionId, fileUUID)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update bitstream metadata into the state
|
* Update bitstream metadata into the state
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user