mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 11:33:04 +00:00
Added more comments
This commit is contained in:
@@ -24,7 +24,7 @@ import {
|
||||
SaveSubmissionFormSuccessAction,
|
||||
SaveSubmissionSectionFormAction,
|
||||
SaveSubmissionSectionFormErrorAction,
|
||||
SaveSubmissionSectionFormSuccessAction,
|
||||
SaveSubmissionSectionFormSuccessAction, SubmissionObjectAction,
|
||||
SubmissionObjectActionTypes,
|
||||
UpdateSectionDataAction
|
||||
} from './submission-objects.actions';
|
||||
@@ -48,6 +48,9 @@ import { SubmissionJsonPatchOperationsService } from '../../core/submission/subm
|
||||
@Injectable()
|
||||
export class SubmissionObjectEffects {
|
||||
|
||||
/**
|
||||
* Dispatch a [InitSectionAction] for every submission sections and dispatch a [CompleteInitSubmissionFormAction]
|
||||
*/
|
||||
@Effect() loadForm$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.INIT_SUBMISSION_FORM),
|
||||
map((action: InitSubmissionFormAction) => {
|
||||
@@ -83,6 +86,9 @@ export class SubmissionObjectEffects {
|
||||
));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Dispatch a [InitSubmissionFormAction]
|
||||
*/
|
||||
@Effect() resetForm$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.RESET_SUBMISSION_FORM),
|
||||
map((action: ResetSubmissionFormAction) =>
|
||||
@@ -95,6 +101,9 @@ export class SubmissionObjectEffects {
|
||||
null
|
||||
)));
|
||||
|
||||
/**
|
||||
* Dispatch a [SaveSubmissionFormSuccessAction] or a [SaveSubmissionFormErrorAction] on error
|
||||
*/
|
||||
@Effect() saveSubmission$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM),
|
||||
switchMap((action: SaveSubmissionFormAction) => {
|
||||
@@ -106,6 +115,9 @@ export class SubmissionObjectEffects {
|
||||
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Dispatch a [SaveForLaterSubmissionFormSuccessAction] or a [SaveSubmissionFormErrorAction] on error
|
||||
*/
|
||||
@Effect() saveForLaterSubmission$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_FOR_LATER_SUBMISSION_FORM),
|
||||
switchMap((action: SaveForLaterSubmissionFormAction) => {
|
||||
@@ -117,6 +129,9 @@ export class SubmissionObjectEffects {
|
||||
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Call parseSaveResponse and dispatch actions
|
||||
*/
|
||||
@Effect() saveSubmissionSuccess$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_SUCCESS, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_SUCCESS),
|
||||
withLatestFrom(this.store$),
|
||||
@@ -125,6 +140,9 @@ export class SubmissionObjectEffects {
|
||||
}),
|
||||
mergeMap((actions) => observableFrom(actions)));
|
||||
|
||||
/**
|
||||
* Dispatch a [SaveSubmissionSectionFormSuccessAction] or a [SaveSubmissionSectionFormErrorAction] on error
|
||||
*/
|
||||
@Effect() saveSection$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM),
|
||||
switchMap((action: SaveSubmissionSectionFormAction) => {
|
||||
@@ -137,11 +155,17 @@ export class SubmissionObjectEffects {
|
||||
catchError(() => observableOf(new SaveSubmissionSectionFormErrorAction(action.payload.submissionId))));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Show a notification on error
|
||||
*/
|
||||
@Effect({dispatch: false}) saveError$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_ERROR, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_ERROR),
|
||||
withLatestFrom(this.store$),
|
||||
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.save_error_notice'))));
|
||||
|
||||
/**
|
||||
* Call parseSaveResponse and dispatch actions or dispatch [SaveSubmissionFormErrorAction] on error
|
||||
*/
|
||||
@Effect() saveAndDeposit$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_AND_DEPOSIT_SUBMISSION),
|
||||
withLatestFrom(this.store$),
|
||||
@@ -161,6 +185,9 @@ export class SubmissionObjectEffects {
|
||||
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Dispatch a [DepositSubmissionSuccessAction] or a [DepositSubmissionErrorAction] on error
|
||||
*/
|
||||
@Effect() depositSubmission$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION),
|
||||
withLatestFrom(this.store$),
|
||||
@@ -170,20 +197,32 @@ export class SubmissionObjectEffects {
|
||||
catchError(() => observableOf(new DepositSubmissionErrorAction(action.payload.submissionId))));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Show a notification on success and redirect to MyDSpace page
|
||||
*/
|
||||
@Effect({dispatch: false}) saveForLaterSubmissionSuccess$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.SAVE_FOR_LATER_SUBMISSION_FORM_SUCCESS),
|
||||
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.save_success_notice'))),
|
||||
tap(() => this.submissionService.redirectToMyDSpace()));
|
||||
|
||||
/**
|
||||
* Show a notification on success and redirect to MyDSpace page
|
||||
*/
|
||||
@Effect({dispatch: false}) depositSubmissionSuccess$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION_SUCCESS),
|
||||
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.deposit_success_notice'))),
|
||||
tap(() => this.submissionService.redirectToMyDSpace()));
|
||||
|
||||
/**
|
||||
* Show a notification on error
|
||||
*/
|
||||
@Effect({dispatch: false}) depositSubmissionError$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION_ERROR),
|
||||
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.deposit_error_notice'))));
|
||||
|
||||
/**
|
||||
* Dispatch a [DiscardSubmissionSuccessAction] or a [DiscardSubmissionErrorAction] on error
|
||||
*/
|
||||
@Effect() discardSubmission$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION),
|
||||
switchMap((action: DepositSubmissionAction) => {
|
||||
@@ -192,11 +231,17 @@ export class SubmissionObjectEffects {
|
||||
catchError(() => observableOf(new DiscardSubmissionErrorAction(action.payload.submissionId))));
|
||||
}));
|
||||
|
||||
/**
|
||||
* Show a notification on success and redirect to MyDSpace page
|
||||
*/
|
||||
@Effect({dispatch: false}) discardSubmissionSuccess$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION_SUCCESS),
|
||||
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.discard_success_notice'))),
|
||||
tap(() => this.submissionService.redirectToMyDSpace()));
|
||||
|
||||
/**
|
||||
* Show a notification on error
|
||||
*/
|
||||
@Effect({dispatch: false}) discardSubmissionError$ = this.actions$.pipe(
|
||||
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION_ERROR),
|
||||
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.discard_error_notice'))));
|
||||
@@ -210,6 +255,12 @@ export class SubmissionObjectEffects {
|
||||
private translate: TranslateService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the submission object retrieved from REST haven't section errors
|
||||
*
|
||||
* @param response
|
||||
* The submission object retrieved from REST
|
||||
*/
|
||||
protected canDeposit(response: SubmissionObject[]) {
|
||||
let canDeposit = true;
|
||||
|
||||
@@ -225,7 +276,26 @@ export class SubmissionObjectEffects {
|
||||
return canDeposit;
|
||||
}
|
||||
|
||||
protected parseSaveResponse(currentState: SubmissionObjectEntry, response: SubmissionObject[], submissionId: string, notify: boolean = true) {
|
||||
/**
|
||||
* Parse the submission object retrieved from REST haven't section errors and return actions to dispatch
|
||||
*
|
||||
* @param currentState
|
||||
* The current SubmissionObjectEntry
|
||||
* @param response
|
||||
* The submission object retrieved from REST
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param notify
|
||||
* A boolean that indicate if show notification or not
|
||||
* @return SubmissionObjectAction[]
|
||||
* List of SubmissionObjectAction to dispatch
|
||||
*/
|
||||
protected parseSaveResponse(
|
||||
currentState: SubmissionObjectEntry,
|
||||
response: SubmissionObject[],
|
||||
submissionId: string,
|
||||
notify: boolean = true): SubmissionObjectAction[] {
|
||||
|
||||
const mappedActions = [];
|
||||
|
||||
if (isNotEmpty(response)) {
|
||||
|
@@ -38,42 +38,138 @@ import { WorkspaceitemSectionDataType } from '../../core/submission/models/works
|
||||
import { WorkspaceitemSectionUploadObject } from '../../core/submission/models/workspaceitem-section-upload.model';
|
||||
import { SectionsType } from '../sections/sections-type';
|
||||
|
||||
/**
|
||||
* An interface to represent section visibility
|
||||
*/
|
||||
export interface SectionVisibility {
|
||||
main: any;
|
||||
other: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to represent section object state
|
||||
*/
|
||||
export interface SubmissionSectionObject {
|
||||
/**
|
||||
* The section header
|
||||
*/
|
||||
header: string;
|
||||
|
||||
/**
|
||||
* The section configuration url
|
||||
*/
|
||||
config: string;
|
||||
|
||||
/**
|
||||
* A boolean representing if this section is mandatory
|
||||
*/
|
||||
mandatory: boolean;
|
||||
|
||||
/**
|
||||
* The section type
|
||||
*/
|
||||
sectionType: SectionsType;
|
||||
|
||||
/**
|
||||
* The section visibility
|
||||
*/
|
||||
visibility: SectionVisibility;
|
||||
|
||||
/**
|
||||
* A boolean representing if this section is collapsed
|
||||
*/
|
||||
collapsed: boolean,
|
||||
|
||||
/**
|
||||
* A boolean representing if this section is enabled
|
||||
*/
|
||||
enabled: boolean;
|
||||
|
||||
/**
|
||||
* The section data object
|
||||
*/
|
||||
data: WorkspaceitemSectionDataType;
|
||||
|
||||
/**
|
||||
* The list of the section errors
|
||||
*/
|
||||
errors: SubmissionSectionError[];
|
||||
|
||||
/**
|
||||
* A boolean representing if this section is loading
|
||||
*/
|
||||
isLoading: boolean;
|
||||
|
||||
/**
|
||||
* A boolean representing if this section is valid
|
||||
*/
|
||||
isValid: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to represent section error
|
||||
*/
|
||||
export interface SubmissionSectionError {
|
||||
/**
|
||||
* A string representing error path
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* The error message
|
||||
*/
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to represent SubmissionSectionObject entry
|
||||
*/
|
||||
export interface SubmissionSectionEntry {
|
||||
[sectionId: string]: SubmissionSectionObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to represent submission object state
|
||||
*/
|
||||
export interface SubmissionObjectEntry {
|
||||
/**
|
||||
* The collection this submission belonging to
|
||||
*/
|
||||
collection?: string,
|
||||
|
||||
/**
|
||||
* The configuration name tha define this submission
|
||||
*/
|
||||
definition?: string,
|
||||
|
||||
/**
|
||||
* The submission self url
|
||||
*/
|
||||
selfUrl?: string;
|
||||
|
||||
/**
|
||||
* The submission active section
|
||||
*/
|
||||
activeSection?: string;
|
||||
|
||||
/**
|
||||
* The list of submission's sections
|
||||
*/
|
||||
sections?: SubmissionSectionEntry;
|
||||
|
||||
/**
|
||||
* A boolean representing if this submission is loading
|
||||
*/
|
||||
isLoading?: boolean;
|
||||
|
||||
/**
|
||||
* A boolean representing if a submission save operation is pending
|
||||
*/
|
||||
savePending?: boolean;
|
||||
|
||||
/**
|
||||
* A boolean representing if a submission deposit operation is pending
|
||||
*/
|
||||
depositPending?: boolean;
|
||||
}
|
||||
|
||||
|
@@ -35,9 +35,20 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
||||
import { SubmissionService } from '../submission.service';
|
||||
import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model';
|
||||
|
||||
/**
|
||||
* A service that provides methods used in submission process.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SectionsService {
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {ScrollToService} scrollToService
|
||||
* @param {SubmissionService} submissionService
|
||||
* @param {Store<SubmissionState>} store
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(private notificationsService: NotificationsService,
|
||||
private scrollToService: ScrollToService,
|
||||
private submissionService: SubmissionService,
|
||||
@@ -45,17 +56,35 @@ export class SectionsService {
|
||||
private translate: TranslateService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the list of the current section errors with the previous one,
|
||||
* and dispatch actions to add/remove to/from the section state
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The workspaceitem self url
|
||||
* @param formId
|
||||
* The [SubmissionDefinitionsModel] that define submission configuration
|
||||
* @param currentErrors
|
||||
* The [SubmissionSectionError] that define submission sections init data
|
||||
* @param prevErrors
|
||||
* The [SubmissionSectionError] that define submission sections init errors
|
||||
*/
|
||||
public checkSectionErrors(
|
||||
submissionId: string,
|
||||
sectionId: string,
|
||||
formId: string,
|
||||
currentErrors: SubmissionSectionError[],
|
||||
prevErrors: SubmissionSectionError[] = []) {
|
||||
// Remove previous error list if the current is empty
|
||||
if (isEmpty(currentErrors)) {
|
||||
this.store.dispatch(new RemoveSectionErrorsAction(submissionId, sectionId));
|
||||
this.store.dispatch(new FormClearErrorsAction(formId));
|
||||
} else if (!isEqual(currentErrors, prevErrors)) {
|
||||
} else if (!isEqual(currentErrors, prevErrors)) { // compare previous error list with the current one
|
||||
const dispatchedErrors = [];
|
||||
|
||||
// Itereate over the current error list
|
||||
currentErrors.forEach((error: SubmissionSectionError) => {
|
||||
const errorPaths: SectionErrorPath[] = parseSectionErrorPaths(error.path);
|
||||
|
||||
@@ -63,7 +92,7 @@ export class SectionsService {
|
||||
if (path.fieldId) {
|
||||
const fieldId = path.fieldId.replace(/\./g, '_');
|
||||
|
||||
// Dispatch action to the form state;
|
||||
// Dispatch action to add form error to the state;
|
||||
const formAddErrorAction = new FormAddError(formId, fieldId, path.fieldIndex, error.message);
|
||||
this.store.dispatch(formAddErrorAction);
|
||||
dispatchedErrors.push(fieldId);
|
||||
@@ -71,6 +100,7 @@ export class SectionsService {
|
||||
});
|
||||
});
|
||||
|
||||
// Itereate over the previous error list
|
||||
prevErrors.forEach((error: SubmissionSectionError) => {
|
||||
const errorPaths: SectionErrorPath[] = parseSectionErrorPaths(error.path);
|
||||
|
||||
@@ -79,6 +109,7 @@ export class SectionsService {
|
||||
const fieldId = path.fieldId.replace(/\./g, '_');
|
||||
|
||||
if (!dispatchedErrors.includes(fieldId)) {
|
||||
// Dispatch action to remove form error from the state;
|
||||
const formRemoveErrorAction = new FormRemoveErrorAction(formId, fieldId, path.fieldIndex);
|
||||
this.store.dispatch(formRemoveErrorAction);
|
||||
}
|
||||
@@ -88,20 +119,58 @@ export class SectionsService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [RemoveSectionErrorsAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
*/
|
||||
public dispatchRemoveSectionErrors(submissionId, sectionId) {
|
||||
this.store.dispatch(new RemoveSectionErrorsAction(submissionId, sectionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data object for the specified section
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<WorkspaceitemSectionDataType>
|
||||
* observable of [WorkspaceitemSectionDataType]
|
||||
*/
|
||||
public getSectionData(submissionId: string, sectionId: string): Observable<WorkspaceitemSectionDataType> {
|
||||
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the error list object data for the specified section
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<SubmissionSectionError>
|
||||
* observable of array of [SubmissionSectionError]
|
||||
*/
|
||||
public getSectionErrors(submissionId: string, sectionId: string): Observable<SubmissionSectionError[]> {
|
||||
return this.store.select(submissionSectionErrorsFromIdSelector(submissionId, sectionId)).pipe(
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state object for the specified section
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<SubmissionSectionObject>
|
||||
* observable of [SubmissionSectionObject]
|
||||
*/
|
||||
public getSectionState(submissionId: string, sectionId: string): Observable<SubmissionSectionObject> {
|
||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||
filter((sectionObj: SubmissionSectionObject) => hasValue(sectionObj)),
|
||||
@@ -109,6 +178,16 @@ export class SectionsService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given section is valid
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<boolean>
|
||||
* Emits true whenever a given section should be valid
|
||||
*/
|
||||
public isSectionValid(submissionId: string, sectionId: string): Observable<boolean> {
|
||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||
filter((sectionObj) => hasValue(sectionObj)),
|
||||
@@ -116,12 +195,32 @@ export class SectionsService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given section is active
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<boolean>
|
||||
* Emits true whenever a given section should be active
|
||||
*/
|
||||
public isSectionActive(submissionId: string, sectionId: string): Observable<boolean> {
|
||||
return this.submissionService.getActiveSectionId(submissionId).pipe(
|
||||
map((activeSectionId: string) => sectionId === activeSectionId),
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given section is enabled
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<boolean>
|
||||
* Emits true whenever a given section should be enabled
|
||||
*/
|
||||
public isSectionEnabled(submissionId: string, sectionId: string): Observable<boolean> {
|
||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||
filter((sectionObj) => hasValue(sectionObj)),
|
||||
@@ -129,6 +228,18 @@ export class SectionsService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given section is a read only section
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @param submissionScope
|
||||
* The submission scope
|
||||
* @return Observable<boolean>
|
||||
* Emits true whenever a given section should be read only
|
||||
*/
|
||||
public isSectionReadOnly(submissionId: string, sectionId: string, submissionScope: SubmissionScopeType): Observable<boolean> {
|
||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||
filter((sectionObj) => hasValue(sectionObj)),
|
||||
@@ -140,6 +251,16 @@ export class SectionsService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given section is a read only available
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @return Observable<boolean>
|
||||
* Emits true whenever a given section should be available
|
||||
*/
|
||||
public isSectionAvailable(submissionId: string, sectionId: string): Observable<boolean> {
|
||||
return this.store.select(submissionObjectFromIdSelector(submissionId)).pipe(
|
||||
filter((submissionState: SubmissionObjectEntry) => isNotUndefined(submissionState)),
|
||||
@@ -149,8 +270,15 @@ export class SectionsService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
public addSection(submissionId: string,
|
||||
sectionId: string) {
|
||||
/**
|
||||
* Dispatch a new [EnableSectionAction] to add a new section and move page target to it
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
*/
|
||||
public addSection(submissionId: string, sectionId: string) {
|
||||
this.store.dispatch(new EnableSectionAction(submissionId, sectionId));
|
||||
const config: ScrollToConfigOptions = {
|
||||
target: sectionId,
|
||||
@@ -160,11 +288,31 @@ export class SectionsService {
|
||||
this.scrollToService.scrollTo(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [DisableSectionAction] to remove section
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
*/
|
||||
public removeSection(submissionId: string, sectionId: string) {
|
||||
this.store.dispatch(new DisableSectionAction(submissionId, sectionId))
|
||||
}
|
||||
|
||||
public updateSectionData(submissionId: string, sectionId: string, data, errors = []) {
|
||||
/**
|
||||
* Dispatch a new [UpdateSectionDataAction] to update section state with new data and errors
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @param data
|
||||
* The section data
|
||||
* @param errors
|
||||
* The list of section errors
|
||||
*/
|
||||
public updateSectionData(submissionId: string, sectionId: string, data: WorkspaceitemSectionDataType, errors: SubmissionSectionError[] = []) {
|
||||
if (isNotEmpty(data)) {
|
||||
const isAvailable$ = this.isSectionAvailable(submissionId, sectionId);
|
||||
const isEnabled$ = this.isSectionEnabled(submissionId, sectionId);
|
||||
@@ -182,10 +330,30 @@ export class SectionsService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [InertSectionErrorsAction] to update section state with new error
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @param error
|
||||
* The section error
|
||||
*/
|
||||
public setSectionError(submissionId: string, sectionId: string, error: SubmissionSectionError) {
|
||||
this.store.dispatch(new InertSectionErrorsAction(submissionId, sectionId, error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [SectionStatusChangeAction] to update section state with new status
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @param status
|
||||
* The section status
|
||||
*/
|
||||
public setSectionStatus(submissionId: string, sectionId: string, status: boolean) {
|
||||
this.store.dispatch(new SectionStatusChangeAction(submissionId, sectionId, status));
|
||||
}
|
||||
|
@@ -777,7 +777,7 @@ describe('SubmissionService test suite', () => {
|
||||
service.notifyNewSection(submissionId, sectionId);
|
||||
flush();
|
||||
|
||||
expect((service as any).notificationsService.info).toHaveBeenCalledWith(null, 'test', null, true);
|
||||
expect((service as any).notificationsService.info).toHaveBeenCalledWith(null, 'submission.sections.general.metadata-extracted-new-section', null, true);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -890,7 +890,7 @@ describe('SubmissionService test suite', () => {
|
||||
const duration = config.submission.autosave.timer * (1000 * 60);
|
||||
|
||||
service.startAutoSave('826');
|
||||
const sub = (service as any).timerObs.subscribe();
|
||||
const sub = (service as any).timer$.subscribe();
|
||||
|
||||
tick(duration / 2);
|
||||
expect((service as any).store.dispatch).not.toHaveBeenCalled();
|
||||
|
@@ -44,12 +44,32 @@ import { RemoteData } from '../core/data/remote-data';
|
||||
import { ErrorResponse } from '../core/cache/response.models';
|
||||
import { RemoteDataError } from '../core/data/remote-data-error';
|
||||
|
||||
/**
|
||||
* A service that provides methods used in submission process.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SubmissionService {
|
||||
|
||||
/**
|
||||
* Subscription
|
||||
*/
|
||||
protected autoSaveSub: Subscription;
|
||||
protected timerObs: Observable<any>;
|
||||
|
||||
/**
|
||||
* Observable used as timer
|
||||
*/
|
||||
protected timer$: Observable<any>;
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
* @param {GlobalConfig} EnvConfig
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {SubmissionRestService} restService
|
||||
* @param {Router} restSerroutervice
|
||||
* @param {RouteService} routeService
|
||||
* @param {Store<SubmissionState>} store
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected restService: SubmissionRestService,
|
||||
@@ -59,28 +79,74 @@ export class SubmissionService {
|
||||
protected translate: TranslateService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [ChangeSubmissionCollectionAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param collectionId
|
||||
* The collection id
|
||||
*/
|
||||
changeSubmissionCollection(submissionId, collectionId) {
|
||||
this.store.dispatch(new ChangeSubmissionCollectionAction(submissionId, collectionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a REST call to create a new workspaceitem and return response
|
||||
*
|
||||
* @return Observable<SubmissionObject>
|
||||
* observable of SubmissionObject
|
||||
*/
|
||||
createSubmission(): Observable<SubmissionObject> {
|
||||
return this.restService.postToEndpoint('workspaceitems', {}).pipe(
|
||||
map((workspaceitem: SubmissionObject) => workspaceitem[0]),
|
||||
catchError(() => observableOf({})))
|
||||
}
|
||||
|
||||
depositSubmission(selfUrl: string): Observable<any> {
|
||||
/**
|
||||
* Perform a REST call to deposit a workspaceitem and return response
|
||||
*
|
||||
* @param selfUrl
|
||||
* The workspaceitem self url
|
||||
* @return Observable<SubmissionObject>
|
||||
* observable of SubmissionObject
|
||||
*/
|
||||
depositSubmission(selfUrl: string): Observable<SubmissionObject[]> {
|
||||
const options: HttpOptions = Object.create({});
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Content-Type', 'text/uri-list');
|
||||
options.headers = headers;
|
||||
return this.restService.postToEndpoint('workflowitems', selfUrl, null, options);
|
||||
return this.restService.postToEndpoint('workflowitems', selfUrl, null, options) as Observable<SubmissionObject[]>;
|
||||
}
|
||||
|
||||
discardSubmission(submissionId: string): Observable<any> {
|
||||
return this.restService.deleteById(submissionId);
|
||||
/**
|
||||
* Perform a REST call to delete a workspaceitem and return response
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<SubmissionObject>
|
||||
* observable of SubmissionObject
|
||||
*/
|
||||
discardSubmission(submissionId: string): Observable<SubmissionObject[]> {
|
||||
return this.restService.deleteById(submissionId) as Observable<SubmissionObject[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [InitSubmissionFormAction]
|
||||
*
|
||||
* @param collectionId
|
||||
* The collection id
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param selfUrl
|
||||
* The workspaceitem self url
|
||||
* @param submissionDefinition
|
||||
* The [SubmissionDefinitionsModel] that define submission configuration
|
||||
* @param sections
|
||||
* The [WorkspaceitemSectionsObject] that define submission sections init data
|
||||
* @param errors
|
||||
* The [SubmissionSectionError] that define submission sections init errors
|
||||
*/
|
||||
dispatchInit(
|
||||
collectionId: string,
|
||||
submissionId: string,
|
||||
@@ -91,36 +157,90 @@ export class SubmissionService {
|
||||
this.store.dispatch(new InitSubmissionFormAction(collectionId, submissionId, selfUrl, submissionDefinition, sections, errors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [SaveAndDepositSubmissionAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
*/
|
||||
dispatchDeposit(submissionId) {
|
||||
this.store.dispatch(new SaveAndDepositSubmissionAction(submissionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [DiscardSubmissionAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
*/
|
||||
dispatchDiscard(submissionId) {
|
||||
this.store.dispatch(new DiscardSubmissionAction(submissionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [SaveSubmissionFormAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
*/
|
||||
dispatchSave(submissionId) {
|
||||
this.store.dispatch(new SaveSubmissionFormAction(submissionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [SaveForLaterSubmissionFormAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
*/
|
||||
dispatchSaveForLater(submissionId) {
|
||||
this.store.dispatch(new SaveForLaterSubmissionFormAction(submissionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [SaveSubmissionSectionFormAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
*/
|
||||
dispatchSaveSection(submissionId, sectionId) {
|
||||
this.store.dispatch(new SaveSubmissionSectionFormAction(submissionId, sectionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id of the current focused section for the specified submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<string>
|
||||
* observable of section id
|
||||
*/
|
||||
getActiveSectionId(submissionId: string): Observable<string> {
|
||||
return this.getSubmissionObject(submissionId).pipe(
|
||||
map((submission: SubmissionObjectEntry) => submission.activeSection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [SubmissionObjectEntry] for the specified submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<SubmissionObjectEntry>
|
||||
* observable of SubmissionObjectEntry
|
||||
*/
|
||||
getSubmissionObject(submissionId: string): Observable<SubmissionObjectEntry> {
|
||||
return this.store.select(submissionObjectFromIdSelector(submissionId)).pipe(
|
||||
filter((submission: SubmissionObjectEntry) => isNotUndefined(submission)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the active [SectionDataObject] belonging to the specified submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<SubmissionObjectEntry>
|
||||
* observable with the list of active submission's sections
|
||||
*/
|
||||
getSubmissionSections(submissionId: string): Observable<SectionDataObject[]> {
|
||||
return this.getSubmissionObject(submissionId).pipe(
|
||||
find((submission: SubmissionObjectEntry) => isNotUndefined(submission.sections) && !submission.isLoading),
|
||||
@@ -146,6 +266,14 @@ export class SubmissionService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the disabled [SectionDataObject] belonging to the specified submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<SubmissionObjectEntry>
|
||||
* observable with the list of disabled submission's sections
|
||||
*/
|
||||
getDisabledSectionsList(submissionId: string): Observable<SectionDataObject[]> {
|
||||
return this.getSubmissionObject(submissionId).pipe(
|
||||
filter((submission: SubmissionObjectEntry) => isNotUndefined(submission.sections) && !submission.isLoading),
|
||||
@@ -167,6 +295,12 @@ export class SubmissionService {
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the correct REST endpoint link path depending on the page route
|
||||
*
|
||||
* @return string
|
||||
* link path
|
||||
*/
|
||||
getSubmissionObjectLinkName(): string {
|
||||
const url = this.router.routerState.snapshot.url;
|
||||
if (url.startsWith('/workspaceitems') || url.startsWith('/submit')) {
|
||||
@@ -178,6 +312,12 @@ export class SubmissionService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the submission scope
|
||||
*
|
||||
* @return SubmissionScopeType
|
||||
* the SubmissionScopeType
|
||||
*/
|
||||
getSubmissionScope(): SubmissionScopeType {
|
||||
let scope: SubmissionScopeType;
|
||||
switch (this.getSubmissionObjectLinkName()) {
|
||||
@@ -194,6 +334,14 @@ export class SubmissionService {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the validity status of the submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<boolean>
|
||||
* observable with submission validity status
|
||||
*/
|
||||
getSubmissionStatus(submissionId: string): Observable<boolean> {
|
||||
return this.store.select(submissionSelector).pipe(
|
||||
map((submissions: SubmissionState) => submissions.objects[submissionId]),
|
||||
@@ -219,6 +367,14 @@ export class SubmissionService {
|
||||
startWith(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the save processing status of the submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<boolean>
|
||||
* observable with submission save processing status
|
||||
*/
|
||||
getSubmissionSaveProcessingStatus(submissionId: string): Observable<boolean> {
|
||||
return this.getSubmissionObject(submissionId).pipe(
|
||||
map((state: SubmissionObjectEntry) => state.savePending),
|
||||
@@ -226,6 +382,14 @@ export class SubmissionService {
|
||||
startWith(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the deposit processing status of the submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<boolean>
|
||||
* observable with submission deposit processing status
|
||||
*/
|
||||
getSubmissionDepositProcessingStatus(submissionId: string): Observable<boolean> {
|
||||
return this.getSubmissionObject(submissionId).pipe(
|
||||
map((state: SubmissionObjectEntry) => state.depositPending),
|
||||
@@ -233,27 +397,50 @@ export class SubmissionService {
|
||||
startWith(false));
|
||||
}
|
||||
|
||||
isSectionHidden(sectionData: SubmissionSectionObject) {
|
||||
/**
|
||||
* Return the visibility status of the specified section
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return boolean
|
||||
* true if section is hidden, false otherwise
|
||||
*/
|
||||
isSectionHidden(sectionData: SubmissionSectionObject): boolean {
|
||||
return (isNotUndefined(sectionData.visibility)
|
||||
&& sectionData.visibility.main === 'HIDDEN'
|
||||
&& sectionData.visibility.other === 'HIDDEN');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loading status of the submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @return Observable<boolean>
|
||||
* observable with submission loading status
|
||||
*/
|
||||
isSubmissionLoading(submissionId: string): Observable<boolean> {
|
||||
return this.getSubmissionObject(submissionId).pipe(
|
||||
map((submission: SubmissionObjectEntry) => submission.isLoading),
|
||||
distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a notification when a new section is added to submission form
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
*/
|
||||
notifyNewSection(submissionId: string, sectionId: string, sectionType?: SectionsType) {
|
||||
this.translate.get('submission.sections.general.metadata-extracted-new-section', { sectionId }).pipe(
|
||||
first())
|
||||
.subscribe((m) => {
|
||||
this.notificationsService.info(null, m, null, true);
|
||||
});
|
||||
const m = this.translate.instant('submission.sections.general.metadata-extracted-new-section', { sectionId });
|
||||
this.notificationsService.info(null, m, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to MyDspace page
|
||||
*/
|
||||
redirectToMyDSpace() {
|
||||
this.routeService.getPreviousUrl().pipe(
|
||||
first()
|
||||
@@ -267,10 +454,27 @@ export class SubmissionService {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [CancelSubmissionFormAction]
|
||||
*/
|
||||
resetAllSubmissionObjects() {
|
||||
this.store.dispatch(new CancelSubmissionFormAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [ResetSubmissionFormAction]
|
||||
*
|
||||
* @param collectionId
|
||||
* The collection id
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param selfUrl
|
||||
* The workspaceitem self url
|
||||
* @param submissionDefinition
|
||||
* The [SubmissionDefinitionsModel] that define submission configuration
|
||||
* @param sections
|
||||
* The [WorkspaceitemSectionsObject] that define submission sections init data
|
||||
*/
|
||||
resetSubmissionObject(
|
||||
collectionId: string,
|
||||
submissionId: string,
|
||||
@@ -281,6 +485,12 @@ export class SubmissionService {
|
||||
this.store.dispatch(new ResetSubmissionFormAction(collectionId, submissionId, selfUrl, sections, submissionDefinition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a REST call to retrieve an existing workspaceitem/workflowitem and return response
|
||||
*
|
||||
* @return Observable<RemoteData<SubmissionObject>>
|
||||
* observable of RemoteData<SubmissionObject>
|
||||
*/
|
||||
retrieveSubmission(submissionId): Observable<RemoteData<SubmissionObject>> {
|
||||
return this.restService.getDataById(this.getSubmissionObjectLinkName(), submissionId).pipe(
|
||||
find((submissionObjects: SubmissionObject[]) => isNotUndefined(submissionObjects)),
|
||||
@@ -302,21 +512,38 @@ export class SubmissionService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a new [SetActiveSectionAction]
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
*/
|
||||
setActiveSection(submissionId, sectionId) {
|
||||
this.store.dispatch(new SetActiveSectionAction(submissionId, sectionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to save automatically the submission
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
*/
|
||||
startAutoSave(submissionId) {
|
||||
this.stopAutoSave();
|
||||
// AUTOSAVE submission
|
||||
// Retrieve interval from config and convert to milliseconds
|
||||
const duration = this.EnvConfig.submission.autosave.timer * (1000 * 60);
|
||||
// Dispatch save action after given duration
|
||||
this.timerObs = observableTimer(duration, duration);
|
||||
this.autoSaveSub = this.timerObs
|
||||
this.timer$ = observableTimer(duration, duration);
|
||||
this.autoSaveSub = this.timer$
|
||||
.subscribe(() => this.store.dispatch(new SaveSubmissionFormAction(submissionId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe subscription to timer
|
||||
*/
|
||||
stopAutoSave() {
|
||||
if (hasValue(this.autoSaveSub)) {
|
||||
this.autoSaveSub.unsubscribe();
|
||||
|
@@ -1,9 +1,28 @@
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* An interface to represent the path of a section error
|
||||
*/
|
||||
export interface SectionErrorPath {
|
||||
|
||||
/**
|
||||
* The section id
|
||||
*/
|
||||
sectionId: string;
|
||||
|
||||
/**
|
||||
* The form field id
|
||||
*/
|
||||
fieldId?: string;
|
||||
|
||||
/**
|
||||
* The form field index
|
||||
*/
|
||||
fieldIndex?: number;
|
||||
|
||||
/**
|
||||
* The complete path
|
||||
*/
|
||||
originalPath: string;
|
||||
}
|
||||
|
||||
@@ -12,7 +31,7 @@ const regex = /([^\/]+)/g;
|
||||
const regexShort = /\/sections\/(.*)/;
|
||||
|
||||
/**
|
||||
* the following method accept an array of section path strings and return a path object
|
||||
* The following method accept an array of section path strings and return a path object
|
||||
* @param {string | string[]} path
|
||||
* @returns {SectionErrorPath[]}
|
||||
*/
|
||||
|
Reference in New Issue
Block a user