mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge remote-tracking branch 'remotes/origin/submission' into mydspace
# Conflicts: # resources/i18n/en.json # src/app/core/data/request.service.spec.ts # src/app/core/data/request.service.ts # src/app/core/json-patch/selectors.ts # src/app/shared/shared.module.ts
This commit is contained in:
@@ -410,6 +410,7 @@
|
|||||||
"f.dateIssued.min": "Start date",
|
"f.dateIssued.min": "Start date",
|
||||||
"f.dateIssued.max": "End date",
|
"f.dateIssued.max": "End date",
|
||||||
"f.subject": "Subject",
|
"f.subject": "Subject",
|
||||||
|
"f.has_content_in_original_bundle": "Has files",
|
||||||
"f.namedresourcetype": "Status",
|
"f.namedresourcetype": "Status",
|
||||||
"f.dateSubmitted": "Date submitted",
|
"f.dateSubmitted": "Date submitted",
|
||||||
"f.itemtype": "Type",
|
"f.itemtype": "Type",
|
||||||
@@ -729,7 +730,7 @@
|
|||||||
"group-collapse": "Collapse",
|
"group-collapse": "Collapse",
|
||||||
"group-expand": "Expand",
|
"group-expand": "Expand",
|
||||||
"group-collapse-help": "Click here to collapse",
|
"group-collapse-help": "Click here to collapse",
|
||||||
"group-expand-help": "Click here to expand and add more element",
|
"group-expand-help": "Click here to expand and add more elements",
|
||||||
"other-information": {
|
"other-information": {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -762,6 +763,34 @@
|
|||||||
"chips": {
|
"chips": {
|
||||||
"remove": "Remove chip"
|
"remove": "Remove chip"
|
||||||
},
|
},
|
||||||
|
"dso-selector": {
|
||||||
|
"create": {
|
||||||
|
"community": {
|
||||||
|
"head": "New community",
|
||||||
|
"sub-level": "Create a new community in",
|
||||||
|
"top-level": "Create a new top-level community"
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"head": "New collection"
|
||||||
|
},
|
||||||
|
"item": {
|
||||||
|
"head": "New item"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"community": {
|
||||||
|
"head": "Edit community"
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"head": "Edit collection"
|
||||||
|
},
|
||||||
|
"item": {
|
||||||
|
"head": "Edit item"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"placeholder": "Search for a {{ type }}",
|
||||||
|
"no-results": "No {{ type }} found"
|
||||||
|
},
|
||||||
"submission": {
|
"submission": {
|
||||||
"general":{
|
"general":{
|
||||||
"cannot_submit": "You have not the privilege to make a new submission.",
|
"cannot_submit": "You have not the privilege to make a new submission.",
|
||||||
@@ -822,14 +851,16 @@
|
|||||||
"upload-successful": "Upload successful",
|
"upload-successful": "Upload successful",
|
||||||
"upload-failed": "Upload failed",
|
"upload-failed": "Upload failed",
|
||||||
"header.policy.default.nolist": "Uploaded files in the {{collectionName}} collection will be accessible according to the following group(s):",
|
"header.policy.default.nolist": "Uploaded files in the {{collectionName}} collection will be accessible according to the following group(s):",
|
||||||
"header.policy.default.withlist": "Please note that uploaded files in the {{collectionName}} collection will be accessible, in addition to what is explicity decided for the single file, with the following group(s):",
|
"header.policy.default.withlist": "Please note that uploaded files in the {{collectionName}} collection will be accessible, in addition to what is explicitly decided for the single file, with the following group(s):",
|
||||||
"form": {
|
"form": {
|
||||||
"access-condition-label": "Access condition type",
|
"access-condition-label": "Access condition type",
|
||||||
"from-label": "Access grant from",
|
"from-label": "Access grant from",
|
||||||
"from-placeholder": "From",
|
"from-placeholder": "From",
|
||||||
"until-label": "Access grant until",
|
"until-label": "Access grant until",
|
||||||
"until-placeholder": "Until",
|
"until-placeholder": "Until",
|
||||||
"group-label": "Group"
|
"group-label": "Group",
|
||||||
|
"group-required": "Group is required.",
|
||||||
|
"date-required": "Date is required."
|
||||||
},
|
},
|
||||||
"save-metadata": "Save metadata",
|
"save-metadata": "Save metadata",
|
||||||
"undo": "Cancel",
|
"undo": "Cancel",
|
||||||
@@ -894,33 +925,5 @@
|
|||||||
"browse": "browse",
|
"browse": "browse",
|
||||||
"queue-lenght": "Queue length",
|
"queue-lenght": "Queue length",
|
||||||
"processing": "Processing"
|
"processing": "Processing"
|
||||||
},
|
|
||||||
"dso-selector": {
|
|
||||||
"create": {
|
|
||||||
"community": {
|
|
||||||
"head": "New community",
|
|
||||||
"sub-level": "Create a new community in",
|
|
||||||
"top-level": "Create a new top-level community"
|
|
||||||
},
|
|
||||||
"collection": {
|
|
||||||
"head": "New collection"
|
|
||||||
},
|
|
||||||
"item": {
|
|
||||||
"head": "New item"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"edit": {
|
|
||||||
"community": {
|
|
||||||
"head": "Edit community"
|
|
||||||
},
|
|
||||||
"collection": {
|
|
||||||
"head": "Edit collection"
|
|
||||||
},
|
|
||||||
"item": {
|
|
||||||
"head": "Edit item"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"placeholder": "Search for a {{ type }}",
|
|
||||||
"no-results": "No {{ type }} found"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Subscription } from 'rxjs';
|
||||||
import { filter, take } from 'rxjs/operators';
|
import { filter, take } from 'rxjs/operators';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
@@ -16,17 +16,34 @@ import { hasValue, isNotEmpty } from '../shared/empty.util';
|
|||||||
import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model';
|
import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model';
|
||||||
import { isAuthenticated } from '../core/auth/selectors';
|
import { isAuthenticated } from '../core/auth/selectors';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents the login page
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-login-page',
|
selector: 'ds-login-page',
|
||||||
styleUrls: ['./login-page.component.scss'],
|
styleUrls: ['./login-page.component.scss'],
|
||||||
templateUrl: './login-page.component.html'
|
templateUrl: './login-page.component.html'
|
||||||
})
|
})
|
||||||
export class LoginPageComponent implements OnDestroy, OnInit {
|
export class LoginPageComponent implements OnDestroy, OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription to unsubscribe onDestroy
|
||||||
|
* @type {Subscription}
|
||||||
|
*/
|
||||||
sub: Subscription;
|
sub: Subscription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ActivatedRoute} route
|
||||||
|
* @param {Store<AppState>} store
|
||||||
|
*/
|
||||||
constructor(private route: ActivatedRoute,
|
constructor(private route: ActivatedRoute,
|
||||||
private store: Store<AppState>) {}
|
private store: Store<AppState>) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const queryParamsObs = this.route.queryParams;
|
const queryParamsObs = this.route.queryParams;
|
||||||
const authenticated = this.store.select(isAuthenticated);
|
const authenticated = this.store.select(isAuthenticated);
|
||||||
@@ -52,6 +69,9 @@ export class LoginPageComponent implements OnDestroy, OnInit {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from subscription
|
||||||
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
if (hasValue(this.sub)) {
|
if (hasValue(this.sub)) {
|
||||||
this.sub.unsubscribe();
|
this.sub.unsubscribe();
|
||||||
|
@@ -13,6 +13,11 @@ export class AccessConditionOption {
|
|||||||
*/
|
*/
|
||||||
groupUUID: string;
|
groupUUID: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The uuid of the Group that contains set of groups this Resource Policy applies to
|
||||||
|
*/
|
||||||
|
selectGroupUUID: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A boolean representing if this Access Condition has a start date
|
* A boolean representing if this Access Condition has a start date
|
||||||
*/
|
*/
|
||||||
|
@@ -6,6 +6,9 @@ import { NormalizedSubmissionFormsModel } from './normalized-config-submission-f
|
|||||||
import { NormalizedSubmissionSectionModel } from './normalized-config-submission-section.model';
|
import { NormalizedSubmissionSectionModel } from './normalized-config-submission-section.model';
|
||||||
import { NormalizedSubmissionUploadsModel } from './normalized-config-submission-uploads.model';
|
import { NormalizedSubmissionUploadsModel } from './normalized-config-submission-uploads.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to return normalized models for config objects
|
||||||
|
*/
|
||||||
export class ConfigObjectFactory {
|
export class ConfigObjectFactory {
|
||||||
public static getConstructor(type): GenericConstructor<ConfigObject> {
|
public static getConstructor(type): GenericConstructor<ConfigObject> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@@ -68,7 +68,7 @@ import { WorkflowitemDataService } from './submission/workflowitem-data.service'
|
|||||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
import { UploaderService } from '../shared/uploader/uploader.service';
|
import { UploaderService } from '../shared/uploader/uploader.service';
|
||||||
import { FileService } from './shared/file.service';
|
import { FileService } from './shared/file.service';
|
||||||
import { SubmissionRestService } from '../submission/submission-rest.service';
|
import { SubmissionRestService } from './submission/submission-rest.service';
|
||||||
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
||||||
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
||||||
import { MetadataschemaParsingService } from './data/metadataschema-parsing.service';
|
import { MetadataschemaParsingService } from './data/metadataschema-parsing.service';
|
||||||
|
@@ -53,6 +53,14 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
|
|
||||||
public abstract getBrowseEndpoint(options: FindAllOptions, linkPath?: string): Observable<string>
|
public abstract getBrowseEndpoint(options: FindAllOptions, linkPath?: string): Observable<string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the HREF with given options object
|
||||||
|
*
|
||||||
|
* @param options The [[FindAllOptions]] object
|
||||||
|
* @param linkPath The link path for the object
|
||||||
|
* @return {Observable<string>}
|
||||||
|
* Return an observable that emits created HREF
|
||||||
|
*/
|
||||||
protected getFindAllHref(options: FindAllOptions = {}, linkPath?: string): Observable<string> {
|
protected getFindAllHref(options: FindAllOptions = {}, linkPath?: string): Observable<string> {
|
||||||
let result: Observable<string>;
|
let result: Observable<string>;
|
||||||
const args = [];
|
const args = [];
|
||||||
@@ -62,6 +70,14 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
return this.buildHrefFromFindOptions(result, args, options);
|
return this.buildHrefFromFindOptions(result, args, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the HREF for a specific object's search method with given options object
|
||||||
|
*
|
||||||
|
* @param searchMethod The search method for the object
|
||||||
|
* @param options The [[FindAllOptions]] object
|
||||||
|
* @return {Observable<string>}
|
||||||
|
* Return an observable that emits created HREF
|
||||||
|
*/
|
||||||
protected getSearchByHref(searchMethod: string, options: FindAllOptions = {}): Observable<string> {
|
protected getSearchByHref(searchMethod: string, options: FindAllOptions = {}): Observable<string> {
|
||||||
let result: Observable<string>;
|
let result: Observable<string>;
|
||||||
const args = [];
|
const args = [];
|
||||||
@@ -77,6 +93,15 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
return this.buildHrefFromFindOptions(result, args, options);
|
return this.buildHrefFromFindOptions(result, args, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn an options object into a query string and combine it with the given HREF
|
||||||
|
*
|
||||||
|
* @param href$ The HREF to which the query string should be appended
|
||||||
|
* @param args Array with additional params to combine with query string
|
||||||
|
* @param options The [[FindAllOptions]] object
|
||||||
|
* @return {Observable<string>}
|
||||||
|
* Return an observable that emits created HREF
|
||||||
|
*/
|
||||||
protected buildHrefFromFindOptions(href$: Observable<string>, args: string[], options: FindAllOptions): Observable<string> {
|
protected buildHrefFromFindOptions(href$: Observable<string>, args: string[], options: FindAllOptions): Observable<string> {
|
||||||
|
|
||||||
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
||||||
@@ -140,12 +165,25 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
return this.rdbService.buildSingle<T>(href);
|
return this.rdbService.buildSingle<T>(href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return object search endpoint by given search method
|
||||||
|
*
|
||||||
|
* @param searchMethod The search method for the object
|
||||||
|
*/
|
||||||
protected getSearchEndpoint(searchMethod: string): Observable<string> {
|
protected getSearchEndpoint(searchMethod: string): Observable<string> {
|
||||||
return this.halService.getEndpoint(`${this.linkPath}/search`).pipe(
|
return this.halService.getEndpoint(`${this.linkPath}/search`).pipe(
|
||||||
filter((href: string) => isNotEmpty(href)),
|
filter((href: string) => isNotEmpty(href)),
|
||||||
map((href: string) => `${href}/${searchMethod}`));
|
map((href: string) => `${href}/${searchMethod}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new FindAllRequest with given search method
|
||||||
|
*
|
||||||
|
* @param searchMethod The search method for the object
|
||||||
|
* @param options The [[FindAllOptions]] object
|
||||||
|
* @return {Observable<RemoteData<PaginatedList<T>>}
|
||||||
|
* Return an observable that emits response from the server
|
||||||
|
*/
|
||||||
protected searchBy(searchMethod: string, options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
protected searchBy(searchMethod: string, options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
|
|
||||||
const hrefObs = this.getSearchByHref(searchMethod, options);
|
const hrefObs = this.getSearchByHref(searchMethod, options);
|
||||||
|
@@ -20,7 +20,6 @@ import {
|
|||||||
} from './request.models';
|
} from './request.models';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
|
||||||
|
|
||||||
describe('RequestService', () => {
|
describe('RequestService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
|
@@ -5,8 +5,8 @@ import { Observable, race as observableRace } from 'rxjs';
|
|||||||
import { filter, find, mergeMap, take } from 'rxjs/operators';
|
import { filter, find, mergeMap, take } from 'rxjs/operators';
|
||||||
import { remove } from 'lodash';
|
import { remove } from 'lodash';
|
||||||
|
|
||||||
import { AppState } from '../../app.reducer';
|
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { AppState } from '../../app.reducer';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
@@ -137,7 +137,6 @@ export class RequestService {
|
|||||||
* @param {RestRequest} request The request to send out
|
* @param {RestRequest} request The request to send out
|
||||||
* @param {boolean} forceBypassCache When true, a new request is always dispatched
|
* @param {boolean} forceBypassCache When true, a new request is always dispatched
|
||||||
*/
|
*/
|
||||||
// TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
|
|
||||||
configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void {
|
configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void {
|
||||||
const isGetRequest = request.method === RestRequestMethod.GET;
|
const isGetRequest = request.method === RestRequestMethod.GET;
|
||||||
if (forceBypassCache) {
|
if (forceBypassCache) {
|
||||||
|
@@ -1,22 +1,50 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||||
import { Group } from './group.model';
|
import { Group } from './group.model';
|
||||||
|
import { RemoteData } from '../../data/remote-data';
|
||||||
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
|
|
||||||
export class EPerson extends DSpaceObject {
|
export class EPerson extends DSpaceObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the unique handle of this Collection
|
||||||
|
*/
|
||||||
public handle: string;
|
public handle: string;
|
||||||
|
|
||||||
public groups: Group[];
|
/**
|
||||||
|
* List of Groups that this EPerson belong to
|
||||||
|
*/
|
||||||
|
public groups: Observable<RemoteData<PaginatedList<Group>>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the netid of this EPerson
|
||||||
|
*/
|
||||||
public netid: string;
|
public netid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the last active date for this EPerson
|
||||||
|
*/
|
||||||
public lastActive: string;
|
public lastActive: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this EPerson can log in
|
||||||
|
*/
|
||||||
public canLogIn: boolean;
|
public canLogIn: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The EPerson email address
|
||||||
|
*/
|
||||||
public email: string;
|
public email: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this EPerson require certificate
|
||||||
|
*/
|
||||||
public requireCertificate: boolean;
|
public requireCertificate: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this EPerson registered itself
|
||||||
|
*/
|
||||||
public selfRegistered: boolean;
|
public selfRegistered: boolean;
|
||||||
|
|
||||||
/** Getter to retrieve the EPerson's full name as a string */
|
/** Getter to retrieve the EPerson's full name as a string */
|
||||||
|
@@ -1,12 +1,28 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||||
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
|
import { RemoteData } from '../../data/remote-data';
|
||||||
|
|
||||||
export class Group extends DSpaceObject {
|
export class Group extends DSpaceObject {
|
||||||
|
|
||||||
public groups: Group[];
|
/**
|
||||||
|
* List of Groups that this Group belong to
|
||||||
|
*/
|
||||||
|
public groups: Observable<RemoteData<PaginatedList<Group>>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the unique handle of this Group
|
||||||
|
*/
|
||||||
public handle: string;
|
public handle: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the name of this Group
|
||||||
|
*/
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the name of this Group is permanent
|
||||||
|
*/
|
||||||
public permanent: boolean;
|
public permanent: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,62 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
||||||
|
|
||||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||||
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
|
||||||
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
|
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
|
||||||
import { EPerson } from './eperson.model';
|
import { EPerson } from './eperson.model';
|
||||||
import { mapsTo } from '../../cache/builders/build-decorators';
|
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
|
||||||
import { Group } from './group.model';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import { NormalizedGroup } from './normalized-group.model';
|
|
||||||
|
|
||||||
@mapsTo(EPerson)
|
@mapsTo(EPerson)
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
@inheritSerialization(NormalizedDSpaceObject)
|
||||||
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject, ListableObject {
|
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject, ListableObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the unique handle of this EPerson
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public handle: string;
|
public handle: string;
|
||||||
|
|
||||||
@autoserializeAs(NormalizedGroup)
|
/**
|
||||||
groups: Group[];
|
* List of Groups that this EPerson belong to
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
@relationship(ResourceType.Group, true)
|
||||||
|
groups: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the netid of this EPerson
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public netid: string;
|
public netid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the last active date for this EPerson
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public lastActive: string;
|
public lastActive: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this EPerson can log in
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public canLogIn: boolean;
|
public canLogIn: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The EPerson email address
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public email: string;
|
public email: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this EPerson require certificate
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public requireCertificate: boolean;
|
public requireCertificate: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this EPerson registered itself
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public selfRegistered: boolean;
|
public selfRegistered: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,38 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
||||||
|
|
||||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||||
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
|
||||||
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
|
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
|
||||||
import { mapsTo } from '../../cache/builders/build-decorators';
|
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
|
||||||
import { Group } from './group.model';
|
import { Group } from './group.model';
|
||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
|
||||||
@mapsTo(Group)
|
@mapsTo(Group)
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
@inheritSerialization(NormalizedDSpaceObject)
|
||||||
export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements CacheableObject, ListableObject {
|
export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements CacheableObject, ListableObject {
|
||||||
|
|
||||||
@autoserializeAs(NormalizedGroup)
|
/**
|
||||||
groups: Group[];
|
* List of Groups that this Group belong to
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
@relationship(ResourceType.Group, true)
|
||||||
|
groups: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the unique handle of this Group
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public handle: string;
|
public handle: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the name of this Group
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representing the name of this Group is permanent
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
public permanent: boolean;
|
public permanent: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,29 +1,8 @@
|
|||||||
// @TODO: Merge with keySelector function present in 'src/app/core/shared/selectors.ts'
|
import { MemoizedSelector } from '@ngrx/store';
|
||||||
import { createSelector, MemoizedSelector, Selector } from '@ngrx/store';
|
|
||||||
import { hasValue } from '../../shared/empty.util';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { coreSelector } from '../core.selectors';
|
import { coreSelector } from '../core.selectors';
|
||||||
import { JsonPatchOperationsEntry, JsonPatchOperationsResourceEntry } from './json-patch-operations.reducer';
|
import { JsonPatchOperationsEntry, JsonPatchOperationsResourceEntry } from './json-patch-operations.reducer';
|
||||||
|
import { keySelector, subStateSelector } from '../../submission/selectors';
|
||||||
export function keySelector<T, V>(parentSelector: Selector<any, any>, subState: string, key: string): MemoizedSelector<T, V> {
|
|
||||||
return createSelector(parentSelector, (state: T) => {
|
|
||||||
if (hasValue(state[subState])) {
|
|
||||||
return state[subState][key];
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function subStateSelector<T, V>(parentSelector: Selector<any, any>, subState: string): MemoizedSelector<T, V> {
|
|
||||||
return createSelector(parentSelector, (state: T) => {
|
|
||||||
if (hasValue(state[subState])) {
|
|
||||||
return state[subState];
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return MemoizedSelector to select all jsonPatchOperations for a specified resource type, stored in the state
|
* Return MemoizedSelector to select all jsonPatchOperations for a specified resource type, stored in the state
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
import { Workspaceitem } from './workspaceitem.model';
|
|
||||||
|
|
||||||
export class EditItem extends Workspaceitem {
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
import { inheritSerialization } from 'cerialize';
|
|
||||||
import { mapsTo } from '../../cache/builders/build-decorators';
|
|
||||||
import { NormalizedSubmissionObject } from './normalized-submission-object.model';
|
|
||||||
import { EditItem } from './edititem.model';
|
|
||||||
|
|
||||||
@mapsTo(EditItem)
|
|
||||||
@inheritSerialization(NormalizedSubmissionObject)
|
|
||||||
export class NormalizedEditItem extends NormalizedSubmissionObject<EditItem> {
|
|
||||||
|
|
||||||
}
|
|
@@ -5,22 +5,37 @@ import { Workflowitem } from './workflowitem.model';
|
|||||||
import { NormalizedSubmissionObject } from './normalized-submission-object.model';
|
import { NormalizedSubmissionObject } from './normalized-submission-object.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An model class for a NormalizedWorkflowItem.
|
||||||
|
*/
|
||||||
@mapsTo(Workflowitem)
|
@mapsTo(Workflowitem)
|
||||||
@inheritSerialization(NormalizedSubmissionObject)
|
@inheritSerialization(NormalizedSubmissionObject)
|
||||||
export class NormalizedWorkflowItem extends NormalizedSubmissionObject<Workflowitem> {
|
export class NormalizedWorkflowItem extends NormalizedSubmissionObject<Workflowitem> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection this workflowitem belonging to
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.Collection, false)
|
@relationship(ResourceType.Collection, false)
|
||||||
collection: string;
|
collection: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The item created with this workflowitem
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.Item, false)
|
@relationship(ResourceType.Item, false)
|
||||||
item: string;
|
item: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration object that define this workflowitem
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.SubmissionDefinition, false)
|
@relationship(ResourceType.SubmissionDefinition, false)
|
||||||
submissionDefinition: string;
|
submissionDefinition: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The EPerson who submit this workflowitem
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.EPerson, false)
|
@relationship(ResourceType.EPerson, false)
|
||||||
submitter: string;
|
submitter: string;
|
||||||
|
@@ -7,23 +7,38 @@ import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-obj
|
|||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import { Workflowitem } from './workflowitem.model';
|
import { Workflowitem } from './workflowitem.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An model class for a NormalizedWorkspaceItem.
|
||||||
|
*/
|
||||||
@mapsTo(Workspaceitem)
|
@mapsTo(Workspaceitem)
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
@inheritSerialization(NormalizedDSpaceObject)
|
||||||
@inheritSerialization(NormalizedSubmissionObject)
|
@inheritSerialization(NormalizedSubmissionObject)
|
||||||
export class NormalizedWorkspaceItem extends NormalizedSubmissionObject<Workflowitem> {
|
export class NormalizedWorkspaceItem extends NormalizedSubmissionObject<Workflowitem> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection this workspaceitem belonging to
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.Collection, false)
|
@relationship(ResourceType.Collection, false)
|
||||||
collection: string;
|
collection: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The item created with this workspaceitem
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.Item, false)
|
@relationship(ResourceType.Item, false)
|
||||||
item: string;
|
item: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration object that define this workspaceitem
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.SubmissionDefinition, false)
|
@relationship(ResourceType.SubmissionDefinition, false)
|
||||||
submissionDefinition: string;
|
submissionDefinition: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The EPerson who submit this workspaceitem
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourceType.EPerson, false)
|
@relationship(ResourceType.EPerson, false)
|
||||||
submitter: string;
|
submitter: string;
|
||||||
|
@@ -46,7 +46,7 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
|
|||||||
sections: WorkspaceitemSectionsObject;
|
sections: WorkspaceitemSectionsObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The submission config definition
|
* The configuration object that define this submission
|
||||||
*/
|
*/
|
||||||
submissionDefinition: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
|
submissionDefinition: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
|
||||||
|
|
||||||
|
@@ -1,7 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* An interface to represent bitstream's access condition.
|
||||||
|
*/
|
||||||
export class SubmissionUploadFileAccessConditionObject {
|
export class SubmissionUploadFileAccessConditionObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The access condition id
|
||||||
|
*/
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The access condition name
|
||||||
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The access group UUID defined in this access condition
|
||||||
|
*/
|
||||||
groupUUID: string;
|
groupUUID: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible start date of the access condition
|
||||||
|
*/
|
||||||
startDate: string;
|
startDate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible end date of the access condition
|
||||||
|
*/
|
||||||
endDate: string;
|
endDate: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
import { Workspaceitem } from './workspaceitem.model';
|
import { Workspaceitem } from './workspaceitem.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model class for a Workflowitem.
|
||||||
|
*/
|
||||||
export class Workflowitem extends Workspaceitem {
|
export class Workflowitem extends Workspaceitem {
|
||||||
}
|
}
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
import { Item } from '../../shared/item.model';
|
|
||||||
|
|
||||||
export interface WorkspaceitemSectionDetectDuplicateObject {
|
|
||||||
matches: {
|
|
||||||
[itemId: string]: DetectDuplicateMatch;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DetectDuplicateMatch {
|
|
||||||
submitterDecision?: string; // [reject|verify]
|
|
||||||
submitterNote?: string;
|
|
||||||
submitterTime?: string; // (readonly)
|
|
||||||
|
|
||||||
workflowDecision?: string; // [reject|verify]
|
|
||||||
workflowNote?: string;
|
|
||||||
workflowTime?: string; // (readonly)
|
|
||||||
|
|
||||||
adminDecision?: string;
|
|
||||||
|
|
||||||
matchObject?: Item;
|
|
||||||
}
|
|
@@ -1,6 +1,10 @@
|
|||||||
import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model';
|
||||||
import { MetadataMapInterface } from '../../shared/metadata.models';
|
import { MetadataMapInterface } from '../../shared/metadata.models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent submission's form section data.
|
||||||
|
* A map of metadata keys to an ordered list of FormFieldMetadataValueObject objects.
|
||||||
|
*/
|
||||||
export interface WorkspaceitemSectionFormObject extends MetadataMapInterface {
|
export interface WorkspaceitemSectionFormObject extends MetadataMapInterface {
|
||||||
[metadata: string]: FormFieldMetadataValueObject[];
|
[metadata: string]: FormFieldMetadataValueObject[];
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,20 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent submission's license section data.
|
||||||
|
*/
|
||||||
export interface WorkspaceitemSectionLicenseObject {
|
export interface WorkspaceitemSectionLicenseObject {
|
||||||
|
/**
|
||||||
|
* The license url
|
||||||
|
*/
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The acceptance date of the license
|
||||||
|
*/
|
||||||
acceptanceDate: string;
|
acceptanceDate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if license has been granted
|
||||||
|
*/
|
||||||
granted: boolean;
|
granted: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model';
|
|
||||||
import { WorkspaceitemSectionUploadFileObject } from './workspaceitem-section-upload-file.model';
|
|
||||||
|
|
||||||
export interface WorkspaceitemSectionRecycleObject {
|
|
||||||
unexpected: any;
|
|
||||||
metadata: FormFieldMetadataValueObject[];
|
|
||||||
files: WorkspaceitemSectionUploadFileObject[];
|
|
||||||
}
|
|
@@ -1,15 +1,46 @@
|
|||||||
import { SubmissionUploadFileAccessConditionObject } from './submission-upload-file-access-condition.model';
|
import { SubmissionUploadFileAccessConditionObject } from './submission-upload-file-access-condition.model';
|
||||||
import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model';
|
import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent submission's upload section file entry.
|
||||||
|
*/
|
||||||
export class WorkspaceitemSectionUploadFileObject {
|
export class WorkspaceitemSectionUploadFileObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file UUID
|
||||||
|
*/
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file metadata
|
||||||
|
*/
|
||||||
metadata: WorkspaceitemSectionFormObject;
|
metadata: WorkspaceitemSectionFormObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file size
|
||||||
|
*/
|
||||||
sizeBytes: number;
|
sizeBytes: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file check sum
|
||||||
|
*/
|
||||||
checkSum: {
|
checkSum: {
|
||||||
checkSumAlgorithm: string;
|
checkSumAlgorithm: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file url
|
||||||
|
*/
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file thumbnail url
|
||||||
|
*/
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of file access conditions
|
||||||
|
*/
|
||||||
accessConditions: SubmissionUploadFileAccessConditionObject[];
|
accessConditions: SubmissionUploadFileAccessConditionObject[];
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,12 @@
|
|||||||
import { WorkspaceitemSectionUploadFileObject } from './workspaceitem-section-upload-file.model';
|
import { WorkspaceitemSectionUploadFileObject } from './workspaceitem-section-upload-file.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent submission's upload section data.
|
||||||
|
*/
|
||||||
export interface WorkspaceitemSectionUploadObject {
|
export interface WorkspaceitemSectionUploadObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of [[WorkspaceitemSectionUploadFileObject]]
|
||||||
|
*/
|
||||||
files: WorkspaceitemSectionUploadFileObject[];
|
files: WorkspaceitemSectionUploadFileObject[];
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,20 @@
|
|||||||
import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model';
|
import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model';
|
||||||
import { WorkspaceitemSectionLicenseObject } from './workspaceitem-section-license.model';
|
import { WorkspaceitemSectionLicenseObject } from './workspaceitem-section-license.model';
|
||||||
import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload.model';
|
import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload.model';
|
||||||
import { WorkspaceitemSectionRecycleObject } from './workspaceitem-section-recycle.model';
|
|
||||||
import { WorkspaceitemSectionDetectDuplicateObject } from './workspaceitem-section-deduplication.model';
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent submission's section object.
|
||||||
|
* A map of section keys to an ordered list of WorkspaceitemSectionDataType objects.
|
||||||
|
*/
|
||||||
export class WorkspaceitemSectionsObject {
|
export class WorkspaceitemSectionsObject {
|
||||||
[name: string]: WorkspaceitemSectionDataType;
|
[name: string]: WorkspaceitemSectionDataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a type alias of all sections
|
||||||
|
*/
|
||||||
export type WorkspaceitemSectionDataType
|
export type WorkspaceitemSectionDataType
|
||||||
= WorkspaceitemSectionUploadObject
|
= WorkspaceitemSectionUploadObject
|
||||||
| WorkspaceitemSectionFormObject
|
| WorkspaceitemSectionFormObject
|
||||||
| WorkspaceitemSectionLicenseObject
|
| WorkspaceitemSectionLicenseObject
|
||||||
| WorkspaceitemSectionRecycleObject
|
|
||||||
| WorkspaceitemSectionDetectDuplicateObject
|
|
||||||
| string;
|
| string;
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { SubmissionObject } from './submission-object.model';
|
import { SubmissionObject } from './submission-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model class for a Workspaceitem.
|
||||||
|
*/
|
||||||
export class Workspaceitem extends SubmissionObject {
|
export class Workspaceitem extends SubmissionObject {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,9 @@ import { SubmitDataResponseDefinitionObject } from '../shared/submit-data-respon
|
|||||||
import { SubmissionPatchRequest } from '../data/request.models';
|
import { SubmissionPatchRequest } from '../data/request.models';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods to make JSON Patch requests.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SubmissionJsonPatchOperationsService extends JsonPatchOperationsService<SubmitDataResponseDefinitionObject, SubmissionPatchRequest> {
|
export class SubmissionJsonPatchOperationsService extends JsonPatchOperationsService<SubmitDataResponseDefinitionObject, SubmissionPatchRequest> {
|
||||||
protected linkPath = '';
|
protected linkPath = '';
|
||||||
|
@@ -11,7 +11,6 @@ export enum SubmissionResourceType {
|
|||||||
Group = 'group',
|
Group = 'group',
|
||||||
WorkspaceItem = 'workspaceitem',
|
WorkspaceItem = 'workspaceitem',
|
||||||
WorkflowItem = 'workflowitem',
|
WorkflowItem = 'workflowitem',
|
||||||
EditItem = 'edititem',
|
|
||||||
SubmissionDefinitions = 'submissiondefinitions',
|
SubmissionDefinitions = 'submissiondefinitions',
|
||||||
SubmissionDefinition = 'submissiondefinition',
|
SubmissionDefinition = 'submissiondefinition',
|
||||||
SubmissionForm = 'submissionform',
|
SubmissionForm = 'submissionform',
|
||||||
|
@@ -13,11 +13,15 @@ import { ObjectCacheService } from '../cache/object-cache.service';
|
|||||||
import { SubmissionResourceType } from './submission-resource-type';
|
import { SubmissionResourceType } from './submission-resource-type';
|
||||||
import { NormalizedWorkspaceItem } from './models/normalized-workspaceitem.model';
|
import { NormalizedWorkspaceItem } from './models/normalized-workspaceitem.model';
|
||||||
import { NormalizedWorkflowItem } from './models/normalized-workflowitem.model';
|
import { NormalizedWorkflowItem } from './models/normalized-workflowitem.model';
|
||||||
import { NormalizedEditItem } from './models/normalized-edititem.model';
|
|
||||||
import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model';
|
||||||
import { SubmissionObject } from './models/submission-object.model';
|
import { SubmissionObject } from './models/submission-object.model';
|
||||||
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a function to check if object has same properties of FormFieldMetadataValueObject
|
||||||
|
*
|
||||||
|
* @param obj
|
||||||
|
*/
|
||||||
export function isServerFormValue(obj: any): boolean {
|
export function isServerFormValue(obj: any): boolean {
|
||||||
return (typeof obj === 'object'
|
return (typeof obj === 'object'
|
||||||
&& obj.hasOwnProperty('value')
|
&& obj.hasOwnProperty('value')
|
||||||
@@ -27,6 +31,11 @@ export function isServerFormValue(obj: any): boolean {
|
|||||||
&& obj.hasOwnProperty('place'))
|
&& obj.hasOwnProperty('place'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a function to normalize sections object of the server response
|
||||||
|
*
|
||||||
|
* @param obj
|
||||||
|
*/
|
||||||
export function normalizeSectionData(obj: any) {
|
export function normalizeSectionData(obj: any) {
|
||||||
let result: any = obj;
|
let result: any = obj;
|
||||||
if (isNotNull(obj)) {
|
if (isNotNull(obj)) {
|
||||||
@@ -74,6 +83,13 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses data from the workspaceitems/workflowitems endpoints
|
||||||
|
*
|
||||||
|
* @param {RestRequest} request
|
||||||
|
* @param {DSpaceRESTV2Response} data
|
||||||
|
* @returns {RestResponse}
|
||||||
|
*/
|
||||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
if (isNotEmpty(data.payload)
|
if (isNotEmpty(data.payload)
|
||||||
&& isNotEmpty(data.payload._links)
|
&& isNotEmpty(data.payload._links)
|
||||||
@@ -93,6 +109,13 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses response and normalize it
|
||||||
|
*
|
||||||
|
* @param {DSpaceRESTV2Response} data
|
||||||
|
* @param {string} requestHref
|
||||||
|
* @returns {any[]}
|
||||||
|
*/
|
||||||
protected processResponse<ObjectDomain, ObjectType>(data: any, requestHref: string): any[] {
|
protected processResponse<ObjectDomain, ObjectType>(data: any, requestHref: string): any[] {
|
||||||
const dataDefinition = this.process<ObjectDomain, ObjectType>(data, requestHref);
|
const dataDefinition = this.process<ObjectDomain, ObjectType>(data, requestHref);
|
||||||
const normalizedDefinition = Array.of();
|
const normalizedDefinition = Array.of();
|
||||||
@@ -103,8 +126,7 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
|
|||||||
let normalizedItem = Object.assign({}, item);
|
let normalizedItem = Object.assign({}, item);
|
||||||
// In case data is an Instance of NormalizedWorkspaceItem normalize field value of all the section of type form
|
// In case data is an Instance of NormalizedWorkspaceItem normalize field value of all the section of type form
|
||||||
if (item instanceof NormalizedWorkspaceItem
|
if (item instanceof NormalizedWorkspaceItem
|
||||||
|| item instanceof NormalizedWorkflowItem
|
|| item instanceof NormalizedWorkflowItem) {
|
||||||
|| item instanceof NormalizedEditItem) {
|
|
||||||
if (item.sections) {
|
if (item.sections) {
|
||||||
const precessedSection = Object.create({});
|
const precessedSection = Object.create({});
|
||||||
// Iterate over all workspaceitem's sections
|
// Iterate over all workspaceitem's sections
|
||||||
|
@@ -2,18 +2,18 @@ import { TestScheduler } from 'rxjs/testing';
|
|||||||
import { getTestScheduler } from 'jasmine-marbles';
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
|
||||||
import { SubmissionRestService } from './submission-rest.service';
|
import { SubmissionRestService } from './submission-rest.service';
|
||||||
import { RequestService } from '../core/data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { getMockRequestService } from '../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { getMockRemoteDataBuildService } from '../shared/mocks/mock-remote-data-build.service';
|
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
||||||
import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service-stub';
|
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
||||||
import {
|
import {
|
||||||
SubmissionDeleteRequest,
|
SubmissionDeleteRequest,
|
||||||
SubmissionPatchRequest,
|
SubmissionPatchRequest,
|
||||||
SubmissionPostRequest,
|
SubmissionPostRequest,
|
||||||
SubmissionRequest
|
SubmissionRequest
|
||||||
} from '../core/data/request.models';
|
} from '../data/request.models';
|
||||||
import { FormFieldMetadataValueObject } from '../shared/form/builder/models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model';
|
||||||
|
|
||||||
describe('SubmissionRestService test suite', () => {
|
describe('SubmissionRestService test suite', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
@@ -3,8 +3,8 @@ import { Injectable } from '@angular/core';
|
|||||||
import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
|
import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, flatMap, map, mergeMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, flatMap, map, mergeMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { RequestService } from '../core/data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { isNotEmpty } from '../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import {
|
import {
|
||||||
DeleteRequest,
|
DeleteRequest,
|
||||||
PostRequest,
|
PostRequest,
|
||||||
@@ -13,14 +13,17 @@ import {
|
|||||||
SubmissionPatchRequest,
|
SubmissionPatchRequest,
|
||||||
SubmissionPostRequest,
|
SubmissionPostRequest,
|
||||||
SubmissionRequest
|
SubmissionRequest
|
||||||
} from '../core/data/request.models';
|
} from '../data/request.models';
|
||||||
import { SubmitDataResponseDefinitionObject } from '../core/shared/submit-data-response-definition.model';
|
import { SubmitDataResponseDefinitionObject } from '../shared/submit-data-response-definition.model';
|
||||||
import { HttpOptions } from '../core/dspace-rest-v2/dspace-rest-v2.service';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../core/cache/response.models';
|
import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../cache/response.models';
|
||||||
import { getResponseFromEntry } from '../core/shared/operators';
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service handling all submission REST requests
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SubmissionRestService {
|
export class SubmissionRestService {
|
||||||
protected linkPath = 'workspaceitems';
|
protected linkPath = 'workspaceitems';
|
||||||
@@ -31,6 +34,14 @@ export class SubmissionRestService {
|
|||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a RestRequest
|
||||||
|
*
|
||||||
|
* @param requestId
|
||||||
|
* The base endpoint for the type of object
|
||||||
|
* @return Observable<SubmitDataResponseDefinitionObject>
|
||||||
|
* server response
|
||||||
|
*/
|
||||||
protected fetchRequest(requestId: string): Observable<SubmitDataResponseDefinitionObject> {
|
protected fetchRequest(requestId: string): Observable<SubmitDataResponseDefinitionObject> {
|
||||||
const responses = this.requestService.getByUUID(requestId).pipe(
|
const responses = this.requestService.getByUUID(requestId).pipe(
|
||||||
getResponseFromEntry()
|
getResponseFromEntry()
|
||||||
@@ -47,10 +58,28 @@ export class SubmissionRestService {
|
|||||||
return observableMerge(errorResponses, successResponses);
|
return observableMerge(errorResponses, successResponses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the HREF for a specific submission object based on its identifier
|
||||||
|
*
|
||||||
|
* @param endpoint
|
||||||
|
* The base endpoint for the type of object
|
||||||
|
* @param resourceID
|
||||||
|
* The identifier for the object
|
||||||
|
*/
|
||||||
protected getEndpointByIDHref(endpoint, resourceID): string {
|
protected getEndpointByIDHref(endpoint, resourceID): string {
|
||||||
return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
|
return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an existing submission Object on the server
|
||||||
|
*
|
||||||
|
* @param scopeId
|
||||||
|
* The submission Object to be removed
|
||||||
|
* @param linkName
|
||||||
|
* The endpoint link name
|
||||||
|
* @return Observable<SubmitDataResponseDefinitionObject>
|
||||||
|
* server response
|
||||||
|
*/
|
||||||
public deleteById(scopeId: string, linkName?: string): Observable<SubmitDataResponseDefinitionObject> {
|
public deleteById(scopeId: string, linkName?: string): Observable<SubmitDataResponseDefinitionObject> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
return this.halService.getEndpoint(linkName || this.linkPath).pipe(
|
return this.halService.getEndpoint(linkName || this.linkPath).pipe(
|
||||||
@@ -59,11 +88,21 @@ export class SubmissionRestService {
|
|||||||
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
|
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
|
||||||
map((endpointURL: string) => new SubmissionDeleteRequest(requestId, endpointURL)),
|
map((endpointURL: string) => new SubmissionDeleteRequest(requestId, endpointURL)),
|
||||||
tap((request: DeleteRequest) => this.requestService.configure(request)),
|
tap((request: DeleteRequest) => this.requestService.configure(request)),
|
||||||
flatMap((request: DeleteRequest) => this.fetchRequest(requestId)),
|
flatMap(() => this.fetchRequest(requestId)),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDataById(linkName: string, id: string): Observable<any> {
|
/**
|
||||||
|
* Return an existing submission Object from the server
|
||||||
|
*
|
||||||
|
* @param linkName
|
||||||
|
* The endpoint link name
|
||||||
|
* @param id
|
||||||
|
* The submission Object to retrieve
|
||||||
|
* @return Observable<SubmitDataResponseDefinitionObject>
|
||||||
|
* server response
|
||||||
|
*/
|
||||||
|
public getDataById(linkName: string, id: string): Observable<SubmitDataResponseDefinitionObject> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
return this.halService.getEndpoint(linkName).pipe(
|
return this.halService.getEndpoint(linkName).pipe(
|
||||||
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, id)),
|
map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, id)),
|
||||||
@@ -71,10 +110,24 @@ export class SubmissionRestService {
|
|||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
map((endpointURL: string) => new SubmissionRequest(requestId, endpointURL)),
|
map((endpointURL: string) => new SubmissionRequest(requestId, endpointURL)),
|
||||||
tap((request: RestRequest) => this.requestService.configure(request, true)),
|
tap((request: RestRequest) => this.requestService.configure(request, true)),
|
||||||
flatMap((request: RestRequest) => this.fetchRequest(requestId)),
|
flatMap(() => this.fetchRequest(requestId)),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new post request
|
||||||
|
*
|
||||||
|
* @param linkName
|
||||||
|
* The endpoint link name
|
||||||
|
* @param body
|
||||||
|
* The post request body
|
||||||
|
* @param scopeId
|
||||||
|
* The submission Object id
|
||||||
|
* @param options
|
||||||
|
* The [HttpOptions] object
|
||||||
|
* @return Observable<SubmitDataResponseDefinitionObject>
|
||||||
|
* server response
|
||||||
|
*/
|
||||||
public postToEndpoint(linkName: string, body: any, scopeId?: string, options?: HttpOptions): Observable<SubmitDataResponseDefinitionObject> {
|
public postToEndpoint(linkName: string, body: any, scopeId?: string, options?: HttpOptions): Observable<SubmitDataResponseDefinitionObject> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
return this.halService.getEndpoint(linkName).pipe(
|
return this.halService.getEndpoint(linkName).pipe(
|
||||||
@@ -83,10 +136,22 @@ export class SubmissionRestService {
|
|||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
map((endpointURL: string) => new SubmissionPostRequest(requestId, endpointURL, body, options)),
|
map((endpointURL: string) => new SubmissionPostRequest(requestId, endpointURL, body, options)),
|
||||||
tap((request: PostRequest) => this.requestService.configure(request)),
|
tap((request: PostRequest) => this.requestService.configure(request)),
|
||||||
flatMap((request: PostRequest) => this.fetchRequest(requestId)),
|
flatMap(() => this.fetchRequest(requestId)),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new patch to a specified object
|
||||||
|
*
|
||||||
|
* @param linkName
|
||||||
|
* The endpoint link name
|
||||||
|
* @param body
|
||||||
|
* The post request body
|
||||||
|
* @param scopeId
|
||||||
|
* The submission Object id
|
||||||
|
* @return Observable<SubmitDataResponseDefinitionObject>
|
||||||
|
* server response
|
||||||
|
*/
|
||||||
public patchToEndpoint(linkName: string, body: any, scopeId?: string): Observable<SubmitDataResponseDefinitionObject> {
|
public patchToEndpoint(linkName: string, body: any, scopeId?: string): Observable<SubmitDataResponseDefinitionObject> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
return this.halService.getEndpoint(linkName).pipe(
|
return this.halService.getEndpoint(linkName).pipe(
|
||||||
@@ -95,7 +160,7 @@ export class SubmissionRestService {
|
|||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
map((endpointURL: string) => new SubmissionPatchRequest(requestId, endpointURL, body)),
|
map((endpointURL: string) => new SubmissionPatchRequest(requestId, endpointURL, body)),
|
||||||
tap((request: PostRequest) => this.requestService.configure(request)),
|
tap((request: PostRequest) => this.requestService.configure(request)),
|
||||||
flatMap((request: PostRequest) => this.fetchRequest(requestId)),
|
flatMap(() => this.fetchRequest(requestId)),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
@@ -1,5 +1,4 @@
|
|||||||
export enum SubmissionScopeType {
|
export enum SubmissionScopeType {
|
||||||
WorkspaceItem = 'WORKSPACE',
|
WorkspaceItem = 'WORKSPACE',
|
||||||
WorkflowItem = 'WORKFLOW',
|
WorkflowItem = 'WORKFLOW'
|
||||||
EditItem = 'ITEM',
|
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,9 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
|||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods to make REST requests with workflowitems endpoint.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkflowitemDataService extends DataService<Workflowitem> {
|
export class WorkflowitemDataService extends DataService<Workflowitem> {
|
||||||
protected linkPath = 'workflowitems';
|
protected linkPath = 'workflowitems';
|
||||||
|
@@ -14,6 +14,9 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
|||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods to make REST requests with workspaceitems endpoint.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceitemDataService extends DataService<Workspaceitem> {
|
export class WorkspaceitemDataService extends DataService<Workspaceitem> {
|
||||||
protected linkPath = 'workspaceitems';
|
protected linkPath = 'workspaceitems';
|
||||||
|
@@ -2,6 +2,9 @@ import { ServerResponseService } from '../shared/services/server-response.servic
|
|||||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
import { AuthService } from '../core/auth/auth.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component representing the `PageNotFound` DSpace page.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-pagenotfound',
|
selector: 'ds-pagenotfound',
|
||||||
styleUrls: ['./pagenotfound.component.scss'],
|
styleUrls: ['./pagenotfound.component.scss'],
|
||||||
@@ -9,10 +12,20 @@ import { AuthService } from '../core/auth/auth.service';
|
|||||||
changeDetection: ChangeDetectionStrategy.Default
|
changeDetection: ChangeDetectionStrategy.Default
|
||||||
})
|
})
|
||||||
export class PageNotFoundComponent implements OnInit {
|
export class PageNotFoundComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {AuthService} authservice
|
||||||
|
* @param {ServerResponseService} responseService
|
||||||
|
*/
|
||||||
constructor(private authservice: AuthService, private responseService: ServerResponseService) {
|
constructor(private authservice: AuthService, private responseService: ServerResponseService) {
|
||||||
this.responseService.setNotFound();
|
this.responseService.setNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove redirect url from the state
|
||||||
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.authservice.clearRedirectUrl();
|
this.authservice.clearRedirectUrl();
|
||||||
}
|
}
|
||||||
|
114
src/app/shared/alert/alert.component.spec.ts
Normal file
114
src/app/shared/alert/alert.component.spec.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { BrowserModule, By } from '@angular/platform-browser';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { AlertComponent } from './alert.component';
|
||||||
|
import { createTestComponent } from '../testing/utils';
|
||||||
|
import { AlertType } from './aletr-type';
|
||||||
|
|
||||||
|
describe('AlertComponent test suite', () => {
|
||||||
|
|
||||||
|
let comp: AlertComponent;
|
||||||
|
let compAsAny: any;
|
||||||
|
let fixture: ComponentFixture<AlertComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
CommonModule,
|
||||||
|
NoopAnimationsModule,
|
||||||
|
TranslateModule.forRoot()
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AlertComponent,
|
||||||
|
TestComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ChangeDetectorRef,
|
||||||
|
AlertComponent
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents().then();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('', () => {
|
||||||
|
let testComp: TestComponent;
|
||||||
|
let testFixture: ComponentFixture<TestComponent>;
|
||||||
|
|
||||||
|
// synchronous beforeEach
|
||||||
|
beforeEach(() => {
|
||||||
|
const html = `
|
||||||
|
<ds-alert [content]="content" [dismissible]="dismissible" [type]="type"></ds-alert>`;
|
||||||
|
|
||||||
|
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
||||||
|
testComp = testFixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
testFixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create AlertComponent', inject([AlertComponent], (app: AlertComponent) => {
|
||||||
|
|
||||||
|
expect(app).toBeDefined();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AlertComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
compAsAny = comp;
|
||||||
|
comp.content = 'test alert';
|
||||||
|
comp.dismissible = true;
|
||||||
|
comp.type = AlertType.Info;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display close icon when dismissible is true', () => {
|
||||||
|
|
||||||
|
const btn = fixture.debugElement.query(By.css('.close'));
|
||||||
|
expect(btn).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not display close icon when dismissible is false', () => {
|
||||||
|
comp.dismissible = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const btn = fixture.debugElement.query(By.css('.close'));
|
||||||
|
expect(btn).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dismiss alert when click on close icon', () => {
|
||||||
|
spyOn(comp, 'dismiss');
|
||||||
|
const btn = fixture.debugElement.query(By.css('.close'));
|
||||||
|
|
||||||
|
btn.nativeElement.click();
|
||||||
|
|
||||||
|
expect(comp.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
comp = null;
|
||||||
|
compAsAny = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// declare a test component
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-test-cmp',
|
||||||
|
template: ``
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
|
||||||
|
content = 'test alert';
|
||||||
|
dismissible = true;
|
||||||
|
type = AlertType.Info;
|
||||||
|
}
|
@@ -1,9 +1,12 @@
|
|||||||
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { trigger } from '@angular/animations';
|
import { trigger } from '@angular/animations';
|
||||||
|
|
||||||
import { AlertType } from './aletrs-type';
|
import { AlertType } from './aletr-type';
|
||||||
import { fadeOutLeave, fadeOutState } from '../animations/fade';
|
import { fadeOutLeave, fadeOutState } from '../animations/fade';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component allow to create div that uses the Bootstrap's Alerts component.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-alert',
|
selector: 'ds-alert',
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
@@ -12,23 +15,52 @@ import { fadeOutLeave, fadeOutState } from '../animations/fade';
|
|||||||
fadeOutLeave, fadeOutState,
|
fadeOutLeave, fadeOutState,
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
templateUrl: './alerts.component.html',
|
templateUrl: './alert.component.html',
|
||||||
styleUrls: ['./alerts.component.scss']
|
styleUrls: ['./alert.component.scss']
|
||||||
})
|
})
|
||||||
|
export class AlertComponent {
|
||||||
|
|
||||||
export class AlertsComponent {
|
/**
|
||||||
|
* The alert content
|
||||||
|
*/
|
||||||
@Input() content: string;
|
@Input() content: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if alert is dismissible
|
||||||
|
*/
|
||||||
@Input() dismissible = false;
|
@Input() dismissible = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The alert type
|
||||||
|
*/
|
||||||
@Input() type: AlertType;
|
@Input() type: AlertType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event fired when alert is dismissed.
|
||||||
|
*/
|
||||||
@Output() close: EventEmitter<any> = new EventEmitter<any>();
|
@Output() close: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The initial animation name
|
||||||
|
*/
|
||||||
public animate = 'fadeIn';
|
public animate = 'fadeIn';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if alert is dismissed or not
|
||||||
|
*/
|
||||||
public dismissed = false;
|
public dismissed = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} cdr
|
||||||
|
*/
|
||||||
constructor(private cdr: ChangeDetectorRef) {
|
constructor(private cdr: ChangeDetectorRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismiss div with animation
|
||||||
|
*/
|
||||||
dismiss() {
|
dismiss() {
|
||||||
if (this.dismissible) {
|
if (this.dismissible) {
|
||||||
this.animate = 'fadeOut';
|
this.animate = 'fadeOut';
|
@@ -19,25 +19,55 @@ import { isNotEmpty, isNull } from '../empty.util';
|
|||||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||||
import { ConfidenceIconConfig } from '../../../config/submission-config.interface';
|
import { ConfidenceIconConfig } from '../../../config/submission-config.interface';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive to add to the element a bootstrap utility class based on metadata confidence value
|
||||||
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[dsAuthorityConfidenceState]'
|
selector: '[dsAuthorityConfidenceState]'
|
||||||
})
|
})
|
||||||
export class AuthorityConfidenceStateDirective implements OnChanges {
|
export class AuthorityConfidenceStateDirective implements OnChanges {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The metadata value
|
||||||
|
*/
|
||||||
@Input() authorityValue: AuthorityValue | FormFieldMetadataValueObject | string;
|
@Input() authorityValue: AuthorityValue | FormFieldMetadataValueObject | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if to show html icon if authority value is empty
|
||||||
|
*/
|
||||||
@Input() visibleWhenAuthorityEmpty = true;
|
@Input() visibleWhenAuthorityEmpty = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The css class applied before directive changes
|
||||||
|
*/
|
||||||
private previousClass: string = null;
|
private previousClass: string = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The css class applied after directive changes
|
||||||
|
*/
|
||||||
private newClass: string;
|
private newClass: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event fired when click on element that has a confidence value empty or different from CF_ACCEPTED
|
||||||
|
*/
|
||||||
@Output() whenClickOnConfidenceNotAccepted: EventEmitter<ConfidenceType> = new EventEmitter<ConfidenceType>();
|
@Output() whenClickOnConfidenceNotAccepted: EventEmitter<ConfidenceType> = new EventEmitter<ConfidenceType>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener to click event
|
||||||
|
*/
|
||||||
@HostListener('click') onClick() {
|
@HostListener('click') onClick() {
|
||||||
if (isNotEmpty(this.authorityValue) && this.getConfidenceByValue(this.authorityValue) !== ConfidenceType.CF_ACCEPTED) {
|
if (isNotEmpty(this.authorityValue) && this.getConfidenceByValue(this.authorityValue) !== ConfidenceType.CF_ACCEPTED) {
|
||||||
this.whenClickOnConfidenceNotAccepted.emit(this.getConfidenceByValue(this.authorityValue));
|
this.whenClickOnConfidenceNotAccepted.emit(this.getConfidenceByValue(this.authorityValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {GlobalConfig} EnvConfig
|
||||||
|
* @param {ElementRef} elem
|
||||||
|
* @param {Renderer2} renderer
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||||
private elem: ElementRef,
|
private elem: ElementRef,
|
||||||
@@ -45,6 +75,11 @@ export class AuthorityConfidenceStateDirective implements OnChanges {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply css class to element whenever authority value change
|
||||||
|
*
|
||||||
|
* @param {SimpleChanges} changes
|
||||||
|
*/
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (!changes.authorityValue.firstChange) {
|
if (!changes.authorityValue.firstChange) {
|
||||||
this.previousClass = this.getClassByConfidence(this.getConfidenceByValue(changes.authorityValue.previousValue))
|
this.previousClass = this.getClassByConfidence(this.getConfidenceByValue(changes.authorityValue.previousValue))
|
||||||
@@ -59,6 +94,9 @@ export class AuthorityConfidenceStateDirective implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply css class to element after view init
|
||||||
|
*/
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
if (isNull(this.previousClass)) {
|
if (isNull(this.previousClass)) {
|
||||||
this.renderer.addClass(this.elem.nativeElement, this.newClass);
|
this.renderer.addClass(this.elem.nativeElement, this.newClass);
|
||||||
@@ -68,6 +106,11 @@ export class AuthorityConfidenceStateDirective implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return confidence value as ConfidenceType
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
private getConfidenceByValue(value: any): ConfidenceType {
|
private getConfidenceByValue(value: any): ConfidenceType {
|
||||||
let confidence: ConfidenceType = ConfidenceType.CF_UNSET;
|
let confidence: ConfidenceType = ConfidenceType.CF_UNSET;
|
||||||
|
|
||||||
@@ -82,6 +125,11 @@ export class AuthorityConfidenceStateDirective implements OnChanges {
|
|||||||
return confidence;
|
return confidence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the properly css class based on confidence value
|
||||||
|
*
|
||||||
|
* @param confidence
|
||||||
|
*/
|
||||||
private getClassByConfidence(confidence: any): string {
|
private getClassByConfidence(confidence: any): string {
|
||||||
if (!this.visibleWhenAuthorityEmpty && confidence === ConfidenceType.CF_UNSET) {
|
if (!this.visibleWhenAuthorityEmpty && confidence === ConfidenceType.CF_UNSET) {
|
||||||
return 'd-none';
|
return 'd-none';
|
||||||
|
@@ -131,7 +131,7 @@ export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dynamic-form-control',
|
selector: 'ds-dynamic-form-control-container',
|
||||||
styleUrls: ['./ds-dynamic-form-control-container.component.scss'],
|
styleUrls: ['./ds-dynamic-form-control-container.component.scss'],
|
||||||
templateUrl: './ds-dynamic-form-control-container.component.html',
|
templateUrl: './ds-dynamic-form-control-container.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.Default
|
changeDetection: ChangeDetectionStrategy.Default
|
||||||
@@ -180,9 +180,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
|
|||||||
if (changes) {
|
if (changes) {
|
||||||
super.ngOnChanges(changes);
|
super.ngOnChanges(changes);
|
||||||
if (this.model && this.model.placeholder) {
|
if (this.model && this.model.placeholder) {
|
||||||
this.translateService.get(this.model.placeholder).subscribe((placeholder) => {
|
this.model.placeholder = this.translateService.instant(this.model.placeholder);
|
||||||
this.model.placeholder = placeholder;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
<ds-dynamic-form-control *ngFor="let model of formModel; trackBy: trackByFn"
|
<ds-dynamic-form-control-container *ngFor="let model of formModel; trackBy: trackByFn"
|
||||||
[formId]="formId"
|
[formId]="formId"
|
||||||
[group]="formGroup"
|
[group]="formGroup"
|
||||||
[hasErrorMessaging]="model.hasErrorMessages"
|
[hasErrorMessaging]="model.hasErrorMessages"
|
||||||
[hidden]="model.hidden"
|
[hidden]="model.hidden"
|
||||||
[layout]="formLayout"
|
[layout]="formLayout"
|
||||||
[model]="model"
|
[model]="model"
|
||||||
[ngClass]="[getClass(model, 'element', 'host'), getClass(model, 'grid', 'host')]"
|
[ngClass]="[getClass(model, 'element', 'host'), getClass(model, 'grid', 'host')]"
|
||||||
[templates]="templates"
|
[templates]="templates"
|
||||||
(dfBlur)="onEvent($event, 'blur')"
|
(dfBlur)="onEvent($event, 'blur')"
|
||||||
(dfChange)="onEvent($event, 'change')"
|
(dfChange)="onEvent($event, 'change')"
|
||||||
(dfFocus)="onEvent($event, 'focus')"></ds-dynamic-form-control>
|
(dfFocus)="onEvent($event, 'focus')"></ds-dynamic-form-control-container>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container>
|
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container>
|
||||||
|
|
||||||
<ds-dynamic-form-control *ngFor="let _model of groupModel.group"
|
<ds-dynamic-form-control-container *ngFor="let _model of groupModel.group"
|
||||||
[bindId]="false"
|
[bindId]="false"
|
||||||
[context]="groupModel"
|
[context]="groupModel"
|
||||||
[group]="control.at(idx)"
|
[group]="control.at(idx)"
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
(dfBlur)="onBlur($event)"
|
(dfBlur)="onBlur($event)"
|
||||||
(dfChange)="onChange($event)"
|
(dfChange)="onChange($event)"
|
||||||
(dfFocus)="onFocus($event)"
|
(dfFocus)="onFocus($event)"
|
||||||
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control>
|
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control-container>
|
||||||
|
|
||||||
<ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: groupModel"></ng-container>
|
<ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: groupModel"></ng-container>
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-date-picker-inline',
|
selector: 'ds-dynamic-date-picker-inline',
|
||||||
templateUrl: './dynamic-date-picker-inline.component.html'
|
templateUrl: './dynamic-date-picker-inline.component.html'
|
||||||
})
|
})
|
||||||
export class DsDatePickerInlineComponent extends DynamicFormControlComponent {
|
export class DsDatePickerInlineComponent extends DynamicFormControlComponent {
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
[formGroupName]="model.id"
|
[formGroupName]="model.id"
|
||||||
[ngClass]="getClass('element','control')">
|
[ngClass]="getClass('element','control')">
|
||||||
|
|
||||||
<ds-dynamic-form-control *ngFor="let _model of model.group"
|
<ds-dynamic-form-control-container *ngFor="let _model of model.group"
|
||||||
[asBootstrapFormGroup]="true"
|
[asBootstrapFormGroup]="true"
|
||||||
[formId]="formId"
|
[formId]="formId"
|
||||||
[group]="control"
|
[group]="control"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
(dfBlur)="onBlur($event)"
|
(dfBlur)="onBlur($event)"
|
||||||
(dfChange)="onChange($event)"
|
(dfChange)="onChange($event)"
|
||||||
(dfFocus)="onFocus($event)"
|
(dfFocus)="onFocus($event)"
|
||||||
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control>
|
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { map, distinctUntilChanged, filter } from 'rxjs/operators';
|
import { map, distinctUntilChanged, filter } from 'rxjs/operators';
|
||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
|
|
||||||
@@ -82,12 +82,13 @@ export class FormService {
|
|||||||
/**
|
/**
|
||||||
* Method to validate form's fields
|
* Method to validate form's fields
|
||||||
*/
|
*/
|
||||||
public validateAllFormFields(formGroup: FormGroup) {
|
public validateAllFormFields(formGroup: FormGroup | FormArray) {
|
||||||
Object.keys(formGroup.controls).forEach((field) => {
|
Object.keys(formGroup.controls).forEach((field) => {
|
||||||
const control = formGroup.get(field);
|
const control = formGroup.get(field);
|
||||||
if (control instanceof FormControl) {
|
if (control instanceof FormControl) {
|
||||||
control.markAsTouched({ onlySelf: true });
|
control.markAsTouched({ onlySelf: true });
|
||||||
} else if (control instanceof FormGroup) {
|
control.markAsDirty({ onlySelf: true });
|
||||||
|
} else if (control instanceof FormGroup || control instanceof FormArray) {
|
||||||
this.validateAllFormFields(control);
|
this.validateAllFormFields(control);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { SectionFormOperationsService } from '../../submission/sections/form/section-form-operations.service';
|
import { SectionFormOperationsService } from '../../submission/sections/form/section-form-operations.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock for [[FormOperationsService]]
|
||||||
|
*/
|
||||||
export function getMockFormOperationsService(): SectionFormOperationsService {
|
export function getMockFormOperationsService(): SectionFormOperationsService {
|
||||||
return jasmine.createSpyObj('SectionFormOperationsService', {
|
return jasmine.createSpyObj('SectionFormOperationsService', {
|
||||||
dispatchOperationsFromEvent: jasmine.createSpy('dispatchOperationsFromEvent'),
|
dispatchOperationsFromEvent: jasmine.createSpy('dispatchOperationsFromEvent'),
|
||||||
|
@@ -2,6 +2,9 @@ import { of as observableOf } from 'rxjs';
|
|||||||
|
|
||||||
import { FormService } from '../form/form.service';
|
import { FormService } from '../form/form.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock for [[FormService]]
|
||||||
|
*/
|
||||||
export function getMockFormService(
|
export function getMockFormService(
|
||||||
id$: string = 'random_id'
|
id$: string = 'random_id'
|
||||||
): FormService {
|
): FormService {
|
||||||
@@ -12,8 +15,8 @@ export function getMockFormService(
|
|||||||
getForm: observableOf({}),
|
getForm: observableOf({}),
|
||||||
getUniqueId: id$,
|
getUniqueId: id$,
|
||||||
resetForm: {},
|
resetForm: {},
|
||||||
validateAllFormFields: {},
|
validateAllFormFields: jasmine.createSpy('validateAllFormFields'),
|
||||||
isValid: observableOf(true),
|
isValid: jasmine.createSpy('isValid'),
|
||||||
isFormInitialized: observableOf(true)
|
isFormInitialized: observableOf(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock for [[RouterService]]
|
||||||
|
*/
|
||||||
export class MockRouter {
|
export class MockRouter {
|
||||||
public events = observableOf({});
|
public events = observableOf({});
|
||||||
public routerState = {
|
public routerState = {
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
|
import { ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock for [[ScrollToService]]
|
||||||
|
*/
|
||||||
export function getMockScrollToService(): ScrollToService {
|
export function getMockScrollToService(): ScrollToService {
|
||||||
return jasmine.createSpyObj('scrollToService', {
|
return jasmine.createSpyObj('scrollToService', {
|
||||||
scrollTo: jasmine.createSpy('scrollTo')
|
scrollTo: jasmine.createSpy('scrollTo')
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { SubmissionFormsConfigService } from '../../core/config/submission-forms-config.service';
|
import { SubmissionFormsConfigService } from '../../core/config/submission-forms-config.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock for [[SubmissionFormsConfigService]]
|
||||||
|
*/
|
||||||
export function getMockSectionUploadService(): SubmissionFormsConfigService {
|
export function getMockSectionUploadService(): SubmissionFormsConfigService {
|
||||||
return jasmine.createSpyObj('SectionUploadService', {
|
return jasmine.createSpyObj('SectionUploadService', {
|
||||||
getUploadedFileList: jasmine.createSpy('getUploadedFileList'),
|
getUploadedFileList: jasmine.createSpy('getUploadedFileList'),
|
||||||
|
@@ -3,6 +3,7 @@ import { SubmissionDefinitionsModel } from '../../core/config/models/config-subm
|
|||||||
import { PaginatedList } from '../../core/data/paginated-list';
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
|
||||||
|
import { Group } from '../../core/eperson/models/group.model';
|
||||||
|
|
||||||
export const mockSectionsData = {
|
export const mockSectionsData = {
|
||||||
traditionalpageone:{
|
traditionalpageone:{
|
||||||
@@ -1364,7 +1365,7 @@ export const mockAccessConditionOptions = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const mockGroup = {
|
export const mockGroup = Object.assign(new Group(), {
|
||||||
handle: null,
|
handle: null,
|
||||||
permanent: true,
|
permanent: true,
|
||||||
self: 'https://rest.api/dspace-spring-rest/api/eperson/groups/123456-g1',
|
self: 'https://rest.api/dspace-spring-rest/api/eperson/groups/123456-g1',
|
||||||
@@ -1386,7 +1387,7 @@ export const mockGroup = {
|
|||||||
},
|
},
|
||||||
page: []
|
page: []
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
export const mockUploadFiles = [
|
export const mockUploadFiles = [
|
||||||
{
|
{
|
||||||
|
@@ -76,7 +76,7 @@ import { NumberPickerComponent } from './number-picker/number-picker.component';
|
|||||||
import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component';
|
import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component';
|
||||||
import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
|
import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
|
||||||
import { MockAdminGuard } from './mocks/mock-admin-guard.service';
|
import { MockAdminGuard } from './mocks/mock-admin-guard.service';
|
||||||
import { AlertsComponent } from './alerts/alerts.component';
|
import { AlertComponent } from './alert/alert.component';
|
||||||
import { MyDSpaceResultListElementComponent } from './object-list/my-dspace-result-list-element/my-dspace-result-list-element.component';
|
import { MyDSpaceResultListElementComponent } from './object-list/my-dspace-result-list-element/my-dspace-result-list-element.component';
|
||||||
import { MessageBoardComponent } from './message-board/message-board.component';
|
import { MessageBoardComponent } from './message-board/message-board.component';
|
||||||
import { MessageComponent } from './message-board/message/message.component';
|
import { MessageComponent } from './message-board/message/message.component';
|
||||||
@@ -180,7 +180,7 @@ const PIPES = [
|
|||||||
|
|
||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
// put shared components here
|
// put shared components here
|
||||||
AlertsComponent,
|
AlertComponent,
|
||||||
AuthNavMenuComponent,
|
AuthNavMenuComponent,
|
||||||
UserMenuComponent,
|
UserMenuComponent,
|
||||||
ChipsComponent,
|
ChipsComponent,
|
||||||
|
@@ -1,5 +1,13 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pipe that allows to iterate over an object and to access to entry key and value :
|
||||||
|
*
|
||||||
|
* <div *ngFor="let obj of objs | dsObjNgFor">
|
||||||
|
* {{obj.key}} - {{obj.value}}
|
||||||
|
* </div>
|
||||||
|
*
|
||||||
|
*/
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'dsObjNgFor'
|
name: 'dsObjNgFor'
|
||||||
})
|
})
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div class="submission-submit-container" >
|
<div class="submission-submit-container" >
|
||||||
<ds-submission-submit-form [collectionId]="collectionId"
|
<ds-submission-form [collectionId]="collectionId"
|
||||||
[sections]="sections"
|
[sections]="sections"
|
||||||
[selfUrl]="selfUrl"
|
[selfUrl]="selfUrl"
|
||||||
[submissionDefinition]="submissionDefinition"
|
[submissionDefinition]="submissionDefinition"
|
||||||
[submissionId]="submissionId"></ds-submission-submit-form>
|
[submissionId]="submissionId"></ds-submission-form>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -14,17 +14,44 @@ import { SubmissionObject } from '../../core/submission/models/submission-object
|
|||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component allows to edit an existing workspaceitem/workflowitem.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-edit',
|
selector: 'ds-submission-edit',
|
||||||
styleUrls: ['./submission-edit.component.scss'],
|
styleUrls: ['./submission-edit.component.scss'],
|
||||||
templateUrl: './submission-edit.component.html'
|
templateUrl: './submission-edit.component.html'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class SubmissionEditComponent implements OnDestroy, OnInit {
|
export class SubmissionEditComponent implements OnDestroy, OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection id this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public collectionId: string;
|
public collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of submission's sections
|
||||||
|
* @type {WorkspaceitemSectionsObject}
|
||||||
|
*/
|
||||||
public sections: WorkspaceitemSectionsObject;
|
public sections: WorkspaceitemSectionsObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission self url
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public selfUrl: string;
|
public selfUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration object that define this submission
|
||||||
|
* @type {SubmissionDefinitionsModel}
|
||||||
|
*/
|
||||||
public submissionDefinition: SubmissionDefinitionsModel;
|
public submissionDefinition: SubmissionDefinitionsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public submissionId: string;
|
public submissionId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +60,16 @@ export class SubmissionEditComponent implements OnDestroy, OnInit {
|
|||||||
*/
|
*/
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} changeDetectorRef
|
||||||
|
* @param {NotificationsService} notificationsService
|
||||||
|
* @param {ActivatedRoute} route
|
||||||
|
* @param {Router} router
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {TranslateService} translate
|
||||||
|
*/
|
||||||
constructor(private changeDetectorRef: ChangeDetectorRef,
|
constructor(private changeDetectorRef: ChangeDetectorRef,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -41,6 +78,9 @@ export class SubmissionEditComponent implements OnDestroy, OnInit {
|
|||||||
private translate: TranslateService) {
|
private translate: TranslateService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve workspaceitem/workflowitem from server and initialize all instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.subs.push(this.route.paramMap.pipe(
|
this.subs.push(this.route.paramMap.pipe(
|
||||||
switchMap((params: ParamMap) => this.submissionService.retrieveSubmission(params.get('id'))),
|
switchMap((params: ParamMap) => this.submissionService.retrieveSubmission(params.get('id'))),
|
||||||
@@ -70,7 +110,7 @@ export class SubmissionEditComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method provided by Angular. Invoked when the instance is destroyed.
|
* Unsubscribe from all subscriptions
|
||||||
*/
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.subs
|
this.subs
|
||||||
|
@@ -36,24 +36,48 @@ import { SubmissionService } from '../../submission.service';
|
|||||||
import { SubmissionObject } from '../../../core/submission/models/submission-object.model';
|
import { SubmissionObject } from '../../../core/submission/models/submission-object.model';
|
||||||
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent a collection entry
|
||||||
|
*/
|
||||||
interface CollectionListEntryItem {
|
interface CollectionListEntryItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent an entry in the collection list
|
||||||
|
*/
|
||||||
interface CollectionListEntry {
|
interface CollectionListEntry {
|
||||||
communities: CollectionListEntryItem[],
|
communities: CollectionListEntryItem[],
|
||||||
collection: CollectionListEntryItem
|
collection: CollectionListEntryItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component allows to show the current collection the submission belonging to and to change it.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-form-collection',
|
selector: 'ds-submission-form-collection',
|
||||||
styleUrls: ['./submission-form-collection.component.scss'],
|
styleUrls: ['./submission-form-collection.component.scss'],
|
||||||
templateUrl: './submission-form-collection.component.html'
|
templateUrl: './submission-form-collection.component.html'
|
||||||
})
|
})
|
||||||
export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current collection id this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() currentCollectionId: string;
|
@Input() currentCollectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current configuration object that define this submission
|
||||||
|
* @type {SubmissionDefinitionsModel}
|
||||||
|
*/
|
||||||
@Input() currentDefinition: string;
|
@Input() currentDefinition: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() submissionId;
|
@Input() submissionId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,18 +86,69 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
*/
|
*/
|
||||||
@Output() collectionChange: EventEmitter<SubmissionObject> = new EventEmitter<SubmissionObject>();
|
@Output() collectionChange: EventEmitter<SubmissionObject> = new EventEmitter<SubmissionObject>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this dropdown button is disabled
|
||||||
|
* @type {BehaviorSubject<boolean>}
|
||||||
|
*/
|
||||||
public disabled$ = new BehaviorSubject<boolean>(true);
|
public disabled$ = new BehaviorSubject<boolean>(true);
|
||||||
public model: any;
|
|
||||||
|
/**
|
||||||
|
* The search form control
|
||||||
|
* @type {FormControl}
|
||||||
|
*/
|
||||||
public searchField: FormControl = new FormControl();
|
public searchField: FormControl = new FormControl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection list obtained from a search
|
||||||
|
* @type {Observable<CollectionListEntry[]>}
|
||||||
|
*/
|
||||||
public searchListCollection$: Observable<CollectionListEntry[]>;
|
public searchListCollection$: Observable<CollectionListEntry[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selected collection id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public selectedCollectionId: string;
|
public selectedCollectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selected collection name
|
||||||
|
* @type {Observable<string>}
|
||||||
|
*/
|
||||||
public selectedCollectionName$: Observable<string>;
|
public selectedCollectionName$: Observable<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JsonPatchOperationPathCombiner object
|
||||||
|
* @type {JsonPatchOperationPathCombiner}
|
||||||
|
*/
|
||||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if dropdown list is scrollable to the bottom
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private scrollableBottom = false;
|
private scrollableBottom = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if dropdown list is scrollable to the top
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private scrollableTop = false;
|
private scrollableTop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} cdr
|
||||||
|
* @param {CommunityDataService} communityDataService
|
||||||
|
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||||
|
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
*/
|
||||||
constructor(protected cdr: ChangeDetectorRef,
|
constructor(protected cdr: ChangeDetectorRef,
|
||||||
private communityDataService: CommunityDataService,
|
private communityDataService: CommunityDataService,
|
||||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||||
@@ -81,6 +156,13 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
private submissionService: SubmissionService) {
|
private submissionService: SubmissionService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called on mousewheel event, it prevent the page scroll
|
||||||
|
* when arriving at the top/bottom of dropdown menu
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* mousewheel event
|
||||||
|
*/
|
||||||
@HostListener('mousewheel', ['$event']) onMousewheel(event) {
|
@HostListener('mousewheel', ['$event']) onMousewheel(event) {
|
||||||
if (event.wheelDelta > 0 && this.scrollableTop) {
|
if (event.wheelDelta > 0 && this.scrollableTop) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -90,11 +172,19 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if dropdown scrollbar is at the top or bottom of the dropdown list
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
onScroll(event) {
|
onScroll(event) {
|
||||||
this.scrollableBottom = (event.target.scrollTop + event.target.clientHeight === event.target.scrollHeight);
|
this.scrollableBottom = (event.target.scrollTop + event.target.clientHeight === event.target.scrollHeight);
|
||||||
this.scrollableTop = (event.target.scrollTop === 0);
|
this.scrollableTop = (event.target.scrollTop === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize collection list
|
||||||
|
*/
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (hasValue(changes.currentCollectionId)
|
if (hasValue(changes.currentCollectionId)
|
||||||
&& hasValue(changes.currentCollectionId.currentValue)) {
|
&& hasValue(changes.currentCollectionId.currentValue)) {
|
||||||
@@ -153,14 +243,26 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', 'collection');
|
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', 'collection');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions
|
||||||
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a [collectionChange] event when a new collection is selected from list
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the selected [CollectionListEntryItem]
|
||||||
|
*/
|
||||||
onSelect(event) {
|
onSelect(event) {
|
||||||
this.searchField.reset();
|
this.searchField.reset();
|
||||||
this.disabled$.next(true);
|
this.disabled$.next(true);
|
||||||
@@ -181,10 +283,19 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset search form control on dropdown menu close
|
||||||
|
*/
|
||||||
onClose() {
|
onClose() {
|
||||||
this.searchField.reset();
|
this.searchField.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset search form control when dropdown menu is closed
|
||||||
|
*
|
||||||
|
* @param isOpen
|
||||||
|
* Representing if the dropdown menu is open or not.
|
||||||
|
*/
|
||||||
toggled(isOpen: boolean) {
|
toggled(isOpen: boolean) {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
this.searchField.reset();
|
this.searchField.reset();
|
||||||
|
@@ -13,7 +13,7 @@ import { mockSubmissionId } from '../../../shared/mocks/mock-submission';
|
|||||||
import { SubmissionService } from '../../submission.service';
|
import { SubmissionService } from '../../submission.service';
|
||||||
import { SubmissionRestServiceStub } from '../../../shared/testing/submission-rest-service-stub';
|
import { SubmissionRestServiceStub } from '../../../shared/testing/submission-rest-service-stub';
|
||||||
import { SubmissionFormFooterComponent } from './submission-form-footer.component';
|
import { SubmissionFormFooterComponent } from './submission-form-footer.component';
|
||||||
import { SubmissionRestService } from '../../submission-rest.service';
|
import { SubmissionRestService } from '../../../core/submission/submission-rest.service';
|
||||||
import { createTestComponent } from '../../../shared/testing/utils';
|
import { createTestComponent } from '../../../shared/testing/utils';
|
||||||
|
|
||||||
describe('SubmissionFormFooterComponent Component', () => {
|
describe('SubmissionFormFooterComponent Component', () => {
|
||||||
|
@@ -4,11 +4,14 @@ import { Observable, of as observableOf } from 'rxjs';
|
|||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
import { SubmissionRestService } from '../../submission-rest.service';
|
import { SubmissionRestService } from '../../../core/submission/submission-rest.service';
|
||||||
import { SubmissionService } from '../../submission.service';
|
import { SubmissionService } from '../../submission.service';
|
||||||
import { SubmissionScopeType } from '../../../core/submission/submission-scope-type';
|
import { SubmissionScopeType } from '../../../core/submission/submission-scope-type';
|
||||||
import { isNotEmpty } from '../../../shared/empty.util';
|
import { isNotEmpty } from '../../../shared/empty.util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents submission form footer bar.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-form-footer',
|
selector: 'ds-submission-form-footer',
|
||||||
styleUrls: ['./submission-form-footer.component.scss'],
|
styleUrls: ['./submission-form-footer.component.scss'],
|
||||||
@@ -16,18 +19,51 @@ import { isNotEmpty } from '../../../shared/empty.util';
|
|||||||
})
|
})
|
||||||
export class SubmissionFormFooterComponent implements OnChanges {
|
export class SubmissionFormFooterComponent implements OnChanges {
|
||||||
|
|
||||||
@Input() submissionId;
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a submission deposit operation is pending
|
||||||
|
* @type {Observable<boolean>}
|
||||||
|
*/
|
||||||
public processingDepositStatus: Observable<boolean>;
|
public processingDepositStatus: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a submission save operation is pending
|
||||||
|
* @type {Observable<boolean>}
|
||||||
|
*/
|
||||||
public processingSaveStatus: Observable<boolean>;
|
public processingSaveStatus: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if showing deposit and discard buttons
|
||||||
|
* @type {Observable<boolean>}
|
||||||
|
*/
|
||||||
public showDepositAndDiscard: Observable<boolean>;
|
public showDepositAndDiscard: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if submission form is valid or not
|
||||||
|
* @type {Observable<boolean>}
|
||||||
|
*/
|
||||||
private submissionIsInvalid: Observable<boolean> = observableOf(true);
|
private submissionIsInvalid: Observable<boolean> = observableOf(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {NgbModal} modalService
|
||||||
|
* @param {SubmissionRestService} restService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
*/
|
||||||
constructor(private modalService: NgbModal,
|
constructor(private modalService: NgbModal,
|
||||||
private restService: SubmissionRestService,
|
private restService: SubmissionRestService,
|
||||||
private submissionService: SubmissionService) {
|
private submissionService: SubmissionService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables
|
||||||
|
*/
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (isNotEmpty(this.submissionId)) {
|
if (isNotEmpty(this.submissionId)) {
|
||||||
this.submissionIsInvalid = this.submissionService.getSubmissionStatus(this.submissionId).pipe(
|
this.submissionIsInvalid = this.submissionService.getSubmissionStatus(this.submissionId).pipe(
|
||||||
@@ -40,18 +76,30 @@ export class SubmissionFormFooterComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a submission save action
|
||||||
|
*/
|
||||||
save(event) {
|
save(event) {
|
||||||
this.submissionService.dispatchSave(this.submissionId);
|
this.submissionService.dispatchSave(this.submissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a submission save for later action
|
||||||
|
*/
|
||||||
saveLater(event) {
|
saveLater(event) {
|
||||||
this.submissionService.dispatchSaveForLater(this.submissionId);
|
this.submissionService.dispatchSaveForLater(this.submissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a submission deposit action
|
||||||
|
*/
|
||||||
public deposit(event) {
|
public deposit(event) {
|
||||||
this.submissionService.dispatchDeposit(this.submissionId);
|
this.submissionService.dispatchDeposit(this.submissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a submission discard action
|
||||||
|
*/
|
||||||
public confirmDiscard(content) {
|
public confirmDiscard(content) {
|
||||||
this.modalService.open(content).result.then(
|
this.modalService.open(content).result.then(
|
||||||
(result) => {
|
(result) => {
|
||||||
|
@@ -1,30 +1,62 @@
|
|||||||
import { Component, Input, OnInit, } from '@angular/core';
|
import { Component, Input, OnInit, } from '@angular/core';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SectionsService } from '../../sections/sections.service';
|
import { SectionsService } from '../../sections/sections.service';
|
||||||
import { HostWindowService } from '../../../shared/host-window.service';
|
import { HostWindowService } from '../../../shared/host-window.service';
|
||||||
import { SubmissionService } from '../../submission.service';
|
import { SubmissionService } from '../../submission.service';
|
||||||
import { SectionDataObject } from '../../sections/models/section-data.model';
|
import { SectionDataObject } from '../../sections/models/section-data.model';
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component allow to add any new section to submission form
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-form-section-add',
|
selector: 'ds-submission-form-section-add',
|
||||||
styleUrls: [ './submission-form-section-add.component.scss' ],
|
styleUrls: [ './submission-form-section-add.component.scss' ],
|
||||||
templateUrl: './submission-form-section-add.component.html'
|
templateUrl: './submission-form-section-add.component.html'
|
||||||
})
|
})
|
||||||
export class SubmissionFormSectionAddComponent implements OnInit {
|
export class SubmissionFormSectionAddComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection id this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() collectionId: string;
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() submissionId: string;
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The possible section list to add
|
||||||
|
* @type {Observable<SectionDataObject[]>}
|
||||||
|
*/
|
||||||
public sectionList$: Observable<SectionDataObject[]>;
|
public sectionList$: Observable<SectionDataObject[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if there are available sections to add
|
||||||
|
* @type {Observable<boolean>}
|
||||||
|
*/
|
||||||
public hasSections$: Observable<boolean>;
|
public hasSections$: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {SectionsService} sectionService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {HostWindowService} windowService
|
||||||
|
*/
|
||||||
constructor(private sectionService: SectionsService,
|
constructor(private sectionService: SectionsService,
|
||||||
private submissionService: SubmissionService,
|
private submissionService: SubmissionService,
|
||||||
public windowService: HostWindowService) {
|
public windowService: HostWindowService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.sectionList$ = this.submissionService.getDisabledSectionsList(this.submissionId);
|
this.sectionList$ = this.submissionService.getDisabledSectionsList(this.submissionId);
|
||||||
this.hasSections$ = this.sectionList$.pipe(
|
this.hasSections$ = this.sectionList$.pipe(
|
||||||
@@ -32,6 +64,9 @@ export class SubmissionFormSectionAddComponent implements OnInit {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch an action to add a new section
|
||||||
|
*/
|
||||||
addSection(sectionId) {
|
addSection(sectionId) {
|
||||||
this.sectionService.addSection(this.submissionId, sectionId);
|
this.sectionService.addSection(this.submissionId, sectionId);
|
||||||
}
|
}
|
||||||
|
@@ -24,9 +24,9 @@
|
|||||||
<div class="submission-form-content">
|
<div class="submission-form-content">
|
||||||
<ds-loading *ngIf="(isLoading() | async)" message="Loading..."></ds-loading>
|
<ds-loading *ngIf="(isLoading() | async)" message="Loading..."></ds-loading>
|
||||||
<ng-container *ngFor="let object of (submissionSections | async)">
|
<ng-container *ngFor="let object of (submissionSections | async)">
|
||||||
<ds-submission-form-section-container [collectionId]="collectionId"
|
<ds-submission-section-container [collectionId]="collectionId"
|
||||||
[submissionId]="submissionId"
|
[submissionId]="submissionId"
|
||||||
[sectionData]="object"></ds-submission-form-section-container>
|
[sectionData]="object"></ds-submission-section-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!(isLoading() | async)" class="submission-form-footer mt-3 mb-3 position-sticky">
|
<div *ngIf="!(isLoading() | async)" class="submission-form-footer mt-3 mb-3 position-sticky">
|
||||||
|
@@ -63,10 +63,10 @@ describe('SubmissionFormComponent Component', () => {
|
|||||||
// synchronous beforeEach
|
// synchronous beforeEach
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const html = `
|
const html = `
|
||||||
<ds-submission-submit-form [collectionId]="collectionId"
|
<ds-submission-form [collectionId]="collectionId"
|
||||||
[selfUrl]="selfUrl"
|
[selfUrl]="selfUrl"
|
||||||
[submissionDefinition]="submissionDefinition"
|
[submissionDefinition]="submissionDefinition"
|
||||||
[submissionId]="submissionId"></ds-submission-submit-form>`;
|
[submissionId]="submissionId"></ds-submission-form>`;
|
||||||
|
|
||||||
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
||||||
testComp = testFixture.componentInstance;
|
testComp = testFixture.componentInstance;
|
||||||
|
@@ -15,22 +15,68 @@ import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
|||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
import { SubmissionObject } from '../../core/submission/models/submission-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents the submission form.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-submit-form',
|
selector: 'ds-submission-form',
|
||||||
styleUrls: ['./submission-form.component.scss'],
|
styleUrls: ['./submission-form.component.scss'],
|
||||||
templateUrl: './submission-form.component.html',
|
templateUrl: './submission-form.component.html',
|
||||||
})
|
})
|
||||||
export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection id this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() collectionId: string;
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of submission's sections
|
||||||
|
* @type {WorkspaceitemSectionsObject}
|
||||||
|
*/
|
||||||
@Input() sections: WorkspaceitemSectionsObject;
|
@Input() sections: WorkspaceitemSectionsObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission self url
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() selfUrl: string;
|
@Input() selfUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration object that define this submission
|
||||||
|
* @type {SubmissionDefinitionsModel}
|
||||||
|
*/
|
||||||
@Input() submissionDefinition: SubmissionDefinitionsModel;
|
@Input() submissionDefinition: SubmissionDefinitionsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() submissionId: string;
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration id that define this submission
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public definitionId: string;
|
public definitionId: string;
|
||||||
public test = true;
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a submission form is pending
|
||||||
|
* @type {Observable<boolean>}
|
||||||
|
*/
|
||||||
public loading: Observable<boolean> = observableOf(true);
|
public loading: Observable<boolean> = observableOf(true);
|
||||||
public submissionSections: Observable<any>;
|
|
||||||
|
/**
|
||||||
|
* Observable of the list of submission's sections
|
||||||
|
* @type {Observable<WorkspaceitemSectionsObject>}
|
||||||
|
*/
|
||||||
|
public submissionSections: Observable<WorkspaceitemSectionsObject>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The uploader configuration options
|
||||||
|
* @type {UploaderOptions}
|
||||||
|
*/
|
||||||
public uploadFilesOptions: UploaderOptions = {
|
public uploadFilesOptions: UploaderOptions = {
|
||||||
url: '',
|
url: '',
|
||||||
authToken: null,
|
authToken: null,
|
||||||
@@ -38,9 +84,26 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
itemAlias: null
|
itemAlias: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if component is active
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
protected isActive: boolean;
|
protected isActive: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
protected subs: Subscription[] = [];
|
protected subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {AuthService} authService
|
||||||
|
* @param {ChangeDetectorRef} changeDetectorRef
|
||||||
|
* @param {HALEndpointService} halService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
@@ -49,9 +112,14 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables and retrieve form configuration
|
||||||
|
*/
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (this.collectionId && this.submissionId) {
|
if (this.collectionId && this.submissionId) {
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
|
|
||||||
|
// retrieve submission's section list
|
||||||
this.submissionSections = this.submissionService.getSubmissionObject(this.submissionId).pipe(
|
this.submissionSections = this.submissionService.getSubmissionObject(this.submissionId).pipe(
|
||||||
filter(() => this.isActive),
|
filter(() => this.isActive),
|
||||||
map((submission: SubmissionObjectEntry) => submission.isLoading),
|
map((submission: SubmissionObjectEntry) => submission.isLoading),
|
||||||
@@ -65,12 +133,14 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// check if is submission loading
|
||||||
this.loading = this.submissionService.getSubmissionObject(this.submissionId).pipe(
|
this.loading = this.submissionService.getSubmissionObject(this.submissionId).pipe(
|
||||||
filter(() => this.isActive),
|
filter(() => this.isActive),
|
||||||
map((submission: SubmissionObjectEntry) => submission.isLoading),
|
map((submission: SubmissionObjectEntry) => submission.isLoading),
|
||||||
map((isLoading: boolean) => isLoading),
|
map((isLoading: boolean) => isLoading),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
|
|
||||||
|
// init submission state
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
this.halService.getEndpoint('workspaceitems').pipe(
|
this.halService.getEndpoint('workspaceitems').pipe(
|
||||||
filter((href: string) => isNotEmpty(href)),
|
filter((href: string) => isNotEmpty(href)),
|
||||||
@@ -89,10 +159,16 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// start auto save
|
||||||
this.submissionService.startAutoSave(this.submissionId);
|
this.submissionService.startAutoSave(this.submissionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions, destroy instance variables
|
||||||
|
* and reset submission state
|
||||||
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
this.submissionService.stopAutoSave();
|
this.submissionService.stopAutoSave();
|
||||||
@@ -102,6 +178,13 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
.forEach((subscription) => subscription.unsubscribe());
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On collection change reset submission state in case of it has a different
|
||||||
|
* submission definition
|
||||||
|
*
|
||||||
|
* @param submissionObject
|
||||||
|
* new submission object
|
||||||
|
*/
|
||||||
onCollectionChange(submissionObject: SubmissionObject) {
|
onCollectionChange(submissionObject: SubmissionObject) {
|
||||||
this.collectionId = (submissionObject.collection as Collection).id;
|
this.collectionId = (submissionObject.collection as Collection).id;
|
||||||
if (this.definitionId !== (submissionObject.submissionDefinition as SubmissionDefinitionsModel).name) {
|
if (this.definitionId !== (submissionObject.submissionDefinition as SubmissionDefinitionsModel).name) {
|
||||||
@@ -119,10 +202,16 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if submission form is loading
|
||||||
|
*/
|
||||||
isLoading(): Observable<boolean> {
|
isLoading(): Observable<boolean> {
|
||||||
return this.loading;
|
return this.loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if submission form is loading
|
||||||
|
*/
|
||||||
protected getSectionsList(): Observable<any> {
|
protected getSectionsList(): Observable<any> {
|
||||||
return this.submissionService.getSubmissionSections(this.submissionId).pipe(
|
return this.submissionService.getSubmissionSections(this.submissionId).pipe(
|
||||||
filter((sections: SectionDataObject[]) => isNotEmpty(sections)),
|
filter((sections: SectionDataObject[]) => isNotEmpty(sections)),
|
||||||
|
@@ -14,24 +14,72 @@ import { UploaderOptions } from '../../../shared/uploader/uploader-options.model
|
|||||||
import parseSectionErrors from '../../utils/parseSectionErrors';
|
import parseSectionErrors from '../../utils/parseSectionErrors';
|
||||||
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents the drop zone that provides to add files to the submission.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-upload-files',
|
selector: 'ds-submission-upload-files',
|
||||||
templateUrl: './submission-upload-files.component.html',
|
templateUrl: './submission-upload-files.component.html',
|
||||||
})
|
})
|
||||||
export class SubmissionUploadFilesComponent implements OnChanges {
|
export class SubmissionUploadFilesComponent implements OnChanges {
|
||||||
|
|
||||||
@Input() collectionId;
|
/**
|
||||||
@Input() submissionId;
|
* The collection id this submission belonging to
|
||||||
@Input() sectionId;
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The upload section id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() sectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The uploader configuration options
|
||||||
|
* @type {UploaderOptions}
|
||||||
|
*/
|
||||||
@Input() uploadFilesOptions: UploaderOptions;
|
@Input() uploadFilesOptions: UploaderOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if is possible to active drop zone over the document page
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
public enableDragOverDocument = true;
|
public enableDragOverDocument = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i18n message label
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public dropOverDocumentMsg = 'submission.sections.upload.drop-message';
|
public dropOverDocumentMsg = 'submission.sections.upload.drop-message';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i18n message label
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public dropMsg = 'submission.sections.upload.drop-message';
|
public dropMsg = 'submission.sections.upload.drop-message';
|
||||||
|
|
||||||
private subs = [];
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if upload functionality is enabled
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private uploadEnabled: Observable<boolean> = observableOf(false);
|
private uploadEnabled: Observable<boolean> = observableOf(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save submission before to upload a file
|
||||||
|
*/
|
||||||
public onBeforeUpload = () => {
|
public onBeforeUpload = () => {
|
||||||
const sub: Subscription = this.operationsService.jsonPatchByResourceType(
|
const sub: Subscription = this.operationsService.jsonPatchByResourceType(
|
||||||
this.submissionService.getSubmissionObjectLinkName(),
|
this.submissionService.getSubmissionObjectLinkName(),
|
||||||
@@ -42,6 +90,15 @@ export class SubmissionUploadFilesComponent implements OnChanges {
|
|||||||
return sub;
|
return sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {NotificationsService} notificationsService
|
||||||
|
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||||
|
* @param {SectionsService} sectionService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {TranslateService} translate
|
||||||
|
*/
|
||||||
constructor(private notificationsService: NotificationsService,
|
constructor(private notificationsService: NotificationsService,
|
||||||
private operationsService: SubmissionJsonPatchOperationsService,
|
private operationsService: SubmissionJsonPatchOperationsService,
|
||||||
private sectionService: SectionsService,
|
private sectionService: SectionsService,
|
||||||
@@ -49,10 +106,19 @@ export class SubmissionUploadFilesComponent implements OnChanges {
|
|||||||
private translate: TranslateService) {
|
private translate: TranslateService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if upload functionality is enabled
|
||||||
|
*/
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.uploadEnabled = this.sectionService.isSectionAvailable(this.submissionId, this.sectionId);
|
this.uploadEnabled = this.sectionService.isSectionAvailable(this.submissionId, this.sectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the submission object retrieved from REST after upload
|
||||||
|
*
|
||||||
|
* @param workspaceitem
|
||||||
|
* The submission object retrieved from REST
|
||||||
|
*/
|
||||||
public onCompleteItem(workspaceitem: Workspaceitem) {
|
public onCompleteItem(workspaceitem: Workspaceitem) {
|
||||||
// Checks if upload section is enabled so do upload
|
// Checks if upload section is enabled so do upload
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
@@ -61,8 +127,8 @@ export class SubmissionUploadFilesComponent implements OnChanges {
|
|||||||
.subscribe((isUploadEnabled) => {
|
.subscribe((isUploadEnabled) => {
|
||||||
if (isUploadEnabled) {
|
if (isUploadEnabled) {
|
||||||
|
|
||||||
const {sections} = workspaceitem;
|
const { sections } = workspaceitem;
|
||||||
const {errors} = workspaceitem;
|
const { errors } = workspaceitem;
|
||||||
|
|
||||||
const errorsList = parseSectionErrors(errors);
|
const errorsList = parseSectionErrors(errors);
|
||||||
if (sections && isNotEmpty(sections)) {
|
if (sections && isNotEmpty(sections)) {
|
||||||
@@ -87,12 +153,15 @@ export class SubmissionUploadFilesComponent implements OnChanges {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show error notification on upload fails
|
||||||
|
*/
|
||||||
public onUploadError() {
|
public onUploadError() {
|
||||||
this.notificationsService.error(null, this.translate.get('submission.sections.upload.upload-failed'));
|
this.notificationsService.error(null, this.translate.get('submission.sections.upload.upload-failed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method provided by Angular. Invoked when the instance is destroyed.
|
* Unsubscribe from all subscriptions
|
||||||
*/
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.subs
|
this.subs
|
||||||
|
@@ -24,7 +24,7 @@ import {
|
|||||||
SaveSubmissionFormSuccessAction,
|
SaveSubmissionFormSuccessAction,
|
||||||
SaveSubmissionSectionFormAction,
|
SaveSubmissionSectionFormAction,
|
||||||
SaveSubmissionSectionFormErrorAction,
|
SaveSubmissionSectionFormErrorAction,
|
||||||
SaveSubmissionSectionFormSuccessAction,
|
SaveSubmissionSectionFormSuccessAction, SubmissionObjectAction,
|
||||||
SubmissionObjectActionTypes,
|
SubmissionObjectActionTypes,
|
||||||
UpdateSectionDataAction
|
UpdateSectionDataAction
|
||||||
} from './submission-objects.actions';
|
} from './submission-objects.actions';
|
||||||
@@ -48,6 +48,9 @@ import { SubmissionJsonPatchOperationsService } from '../../core/submission/subm
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class SubmissionObjectEffects {
|
export class SubmissionObjectEffects {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a [InitSectionAction] for every submission sections and dispatch a [CompleteInitSubmissionFormAction]
|
||||||
|
*/
|
||||||
@Effect() loadForm$ = this.actions$.pipe(
|
@Effect() loadForm$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.INIT_SUBMISSION_FORM),
|
ofType(SubmissionObjectActionTypes.INIT_SUBMISSION_FORM),
|
||||||
map((action: InitSubmissionFormAction) => {
|
map((action: InitSubmissionFormAction) => {
|
||||||
@@ -83,6 +86,9 @@ export class SubmissionObjectEffects {
|
|||||||
));
|
));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a [InitSubmissionFormAction]
|
||||||
|
*/
|
||||||
@Effect() resetForm$ = this.actions$.pipe(
|
@Effect() resetForm$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.RESET_SUBMISSION_FORM),
|
ofType(SubmissionObjectActionTypes.RESET_SUBMISSION_FORM),
|
||||||
map((action: ResetSubmissionFormAction) =>
|
map((action: ResetSubmissionFormAction) =>
|
||||||
@@ -95,6 +101,9 @@ export class SubmissionObjectEffects {
|
|||||||
null
|
null
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a [SaveSubmissionFormSuccessAction] or a [SaveSubmissionFormErrorAction] on error
|
||||||
|
*/
|
||||||
@Effect() saveSubmission$ = this.actions$.pipe(
|
@Effect() saveSubmission$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM),
|
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM),
|
||||||
switchMap((action: SaveSubmissionFormAction) => {
|
switchMap((action: SaveSubmissionFormAction) => {
|
||||||
@@ -106,6 +115,9 @@ export class SubmissionObjectEffects {
|
|||||||
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a [SaveForLaterSubmissionFormSuccessAction] or a [SaveSubmissionFormErrorAction] on error
|
||||||
|
*/
|
||||||
@Effect() saveForLaterSubmission$ = this.actions$.pipe(
|
@Effect() saveForLaterSubmission$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_FOR_LATER_SUBMISSION_FORM),
|
ofType(SubmissionObjectActionTypes.SAVE_FOR_LATER_SUBMISSION_FORM),
|
||||||
switchMap((action: SaveForLaterSubmissionFormAction) => {
|
switchMap((action: SaveForLaterSubmissionFormAction) => {
|
||||||
@@ -117,6 +129,9 @@ export class SubmissionObjectEffects {
|
|||||||
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call parseSaveResponse and dispatch actions
|
||||||
|
*/
|
||||||
@Effect() saveSubmissionSuccess$ = this.actions$.pipe(
|
@Effect() saveSubmissionSuccess$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_SUCCESS, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_SUCCESS),
|
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_SUCCESS, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_SUCCESS),
|
||||||
withLatestFrom(this.store$),
|
withLatestFrom(this.store$),
|
||||||
@@ -125,6 +140,9 @@ export class SubmissionObjectEffects {
|
|||||||
}),
|
}),
|
||||||
mergeMap((actions) => observableFrom(actions)));
|
mergeMap((actions) => observableFrom(actions)));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a [SaveSubmissionSectionFormSuccessAction] or a [SaveSubmissionSectionFormErrorAction] on error
|
||||||
|
*/
|
||||||
@Effect() saveSection$ = this.actions$.pipe(
|
@Effect() saveSection$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM),
|
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM),
|
||||||
switchMap((action: SaveSubmissionSectionFormAction) => {
|
switchMap((action: SaveSubmissionSectionFormAction) => {
|
||||||
@@ -137,11 +155,17 @@ export class SubmissionObjectEffects {
|
|||||||
catchError(() => observableOf(new SaveSubmissionSectionFormErrorAction(action.payload.submissionId))));
|
catchError(() => observableOf(new SaveSubmissionSectionFormErrorAction(action.payload.submissionId))));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on error
|
||||||
|
*/
|
||||||
@Effect({dispatch: false}) saveError$ = this.actions$.pipe(
|
@Effect({dispatch: false}) saveError$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_ERROR, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_ERROR),
|
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_ERROR, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_ERROR),
|
||||||
withLatestFrom(this.store$),
|
withLatestFrom(this.store$),
|
||||||
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.save_error_notice'))));
|
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(
|
@Effect() saveAndDeposit$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_AND_DEPOSIT_SUBMISSION),
|
ofType(SubmissionObjectActionTypes.SAVE_AND_DEPOSIT_SUBMISSION),
|
||||||
withLatestFrom(this.store$),
|
withLatestFrom(this.store$),
|
||||||
@@ -161,6 +185,9 @@ export class SubmissionObjectEffects {
|
|||||||
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId))));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a [DepositSubmissionSuccessAction] or a [DepositSubmissionErrorAction] on error
|
||||||
|
*/
|
||||||
@Effect() depositSubmission$ = this.actions$.pipe(
|
@Effect() depositSubmission$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION),
|
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION),
|
||||||
withLatestFrom(this.store$),
|
withLatestFrom(this.store$),
|
||||||
@@ -170,20 +197,32 @@ export class SubmissionObjectEffects {
|
|||||||
catchError(() => observableOf(new DepositSubmissionErrorAction(action.payload.submissionId))));
|
catchError(() => observableOf(new DepositSubmissionErrorAction(action.payload.submissionId))));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on success and redirect to MyDSpace page
|
||||||
|
*/
|
||||||
@Effect({dispatch: false}) saveForLaterSubmissionSuccess$ = this.actions$.pipe(
|
@Effect({dispatch: false}) saveForLaterSubmissionSuccess$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.SAVE_FOR_LATER_SUBMISSION_FORM_SUCCESS),
|
ofType(SubmissionObjectActionTypes.SAVE_FOR_LATER_SUBMISSION_FORM_SUCCESS),
|
||||||
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.save_success_notice'))),
|
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.save_success_notice'))),
|
||||||
tap(() => this.submissionService.redirectToMyDSpace()));
|
tap(() => this.submissionService.redirectToMyDSpace()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on success and redirect to MyDSpace page
|
||||||
|
*/
|
||||||
@Effect({dispatch: false}) depositSubmissionSuccess$ = this.actions$.pipe(
|
@Effect({dispatch: false}) depositSubmissionSuccess$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION_SUCCESS),
|
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION_SUCCESS),
|
||||||
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.deposit_success_notice'))),
|
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.deposit_success_notice'))),
|
||||||
tap(() => this.submissionService.redirectToMyDSpace()));
|
tap(() => this.submissionService.redirectToMyDSpace()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on error
|
||||||
|
*/
|
||||||
@Effect({dispatch: false}) depositSubmissionError$ = this.actions$.pipe(
|
@Effect({dispatch: false}) depositSubmissionError$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION_ERROR),
|
ofType(SubmissionObjectActionTypes.DEPOSIT_SUBMISSION_ERROR),
|
||||||
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.deposit_error_notice'))));
|
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(
|
@Effect() discardSubmission$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION),
|
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION),
|
||||||
switchMap((action: DepositSubmissionAction) => {
|
switchMap((action: DepositSubmissionAction) => {
|
||||||
@@ -192,11 +231,17 @@ export class SubmissionObjectEffects {
|
|||||||
catchError(() => observableOf(new DiscardSubmissionErrorAction(action.payload.submissionId))));
|
catchError(() => observableOf(new DiscardSubmissionErrorAction(action.payload.submissionId))));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on success and redirect to MyDSpace page
|
||||||
|
*/
|
||||||
@Effect({dispatch: false}) discardSubmissionSuccess$ = this.actions$.pipe(
|
@Effect({dispatch: false}) discardSubmissionSuccess$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION_SUCCESS),
|
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION_SUCCESS),
|
||||||
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.discard_success_notice'))),
|
tap(() => this.notificationsService.success(null, this.translate.get('submission.sections.general.discard_success_notice'))),
|
||||||
tap(() => this.submissionService.redirectToMyDSpace()));
|
tap(() => this.submissionService.redirectToMyDSpace()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on error
|
||||||
|
*/
|
||||||
@Effect({dispatch: false}) discardSubmissionError$ = this.actions$.pipe(
|
@Effect({dispatch: false}) discardSubmissionError$ = this.actions$.pipe(
|
||||||
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION_ERROR),
|
ofType(SubmissionObjectActionTypes.DISCARD_SUBMISSION_ERROR),
|
||||||
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.discard_error_notice'))));
|
tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.discard_error_notice'))));
|
||||||
@@ -210,6 +255,12 @@ export class SubmissionObjectEffects {
|
|||||||
private translate: TranslateService) {
|
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[]) {
|
protected canDeposit(response: SubmissionObject[]) {
|
||||||
let canDeposit = true;
|
let canDeposit = true;
|
||||||
|
|
||||||
@@ -225,7 +276,26 @@ export class SubmissionObjectEffects {
|
|||||||
return canDeposit;
|
return canDeposit;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected parseSaveResponse(currentState: SubmissionObjectEntry, response: SubmissionObject[], submissionId: string, notify: boolean = true) {
|
/**
|
||||||
|
* Parse the submission object retrieved from REST 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 = [];
|
const mappedActions = [];
|
||||||
|
|
||||||
if (isNotEmpty(response)) {
|
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 { WorkspaceitemSectionUploadObject } from '../../core/submission/models/workspaceitem-section-upload.model';
|
||||||
import { SectionsType } from '../sections/sections-type';
|
import { SectionsType } from '../sections/sections-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent section visibility
|
||||||
|
*/
|
||||||
export interface SectionVisibility {
|
export interface SectionVisibility {
|
||||||
main: any;
|
main: any;
|
||||||
other: any;
|
other: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent section object state
|
||||||
|
*/
|
||||||
export interface SubmissionSectionObject {
|
export interface SubmissionSectionObject {
|
||||||
|
/**
|
||||||
|
* The section header
|
||||||
|
*/
|
||||||
header: string;
|
header: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section configuration url
|
||||||
|
*/
|
||||||
config: string;
|
config: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is mandatory
|
||||||
|
*/
|
||||||
mandatory: boolean;
|
mandatory: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section type
|
||||||
|
*/
|
||||||
sectionType: SectionsType;
|
sectionType: SectionsType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section visibility
|
||||||
|
*/
|
||||||
visibility: SectionVisibility;
|
visibility: SectionVisibility;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is collapsed
|
||||||
|
*/
|
||||||
collapsed: boolean,
|
collapsed: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is enabled
|
||||||
|
*/
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section data object
|
||||||
|
*/
|
||||||
data: WorkspaceitemSectionDataType;
|
data: WorkspaceitemSectionDataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of the section errors
|
||||||
|
*/
|
||||||
errors: SubmissionSectionError[];
|
errors: SubmissionSectionError[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is loading
|
||||||
|
*/
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is valid
|
||||||
|
*/
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent section error
|
||||||
|
*/
|
||||||
export interface SubmissionSectionError {
|
export interface SubmissionSectionError {
|
||||||
|
/**
|
||||||
|
* A string representing error path
|
||||||
|
*/
|
||||||
path: string;
|
path: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error message
|
||||||
|
*/
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent SubmissionSectionObject entry
|
||||||
|
*/
|
||||||
export interface SubmissionSectionEntry {
|
export interface SubmissionSectionEntry {
|
||||||
[sectionId: string]: SubmissionSectionObject;
|
[sectionId: string]: SubmissionSectionObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent submission object state
|
||||||
|
*/
|
||||||
export interface SubmissionObjectEntry {
|
export interface SubmissionObjectEntry {
|
||||||
|
/**
|
||||||
|
* The collection this submission belonging to
|
||||||
|
*/
|
||||||
collection?: string,
|
collection?: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration name that define this submission
|
||||||
|
*/
|
||||||
definition?: string,
|
definition?: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission self url
|
||||||
|
*/
|
||||||
selfUrl?: string;
|
selfUrl?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission active section
|
||||||
|
*/
|
||||||
activeSection?: string;
|
activeSection?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of submission's sections
|
||||||
|
*/
|
||||||
sections?: SubmissionSectionEntry;
|
sections?: SubmissionSectionEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this submission is loading
|
||||||
|
*/
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a submission save operation is pending
|
||||||
|
*/
|
||||||
savePending?: boolean;
|
savePending?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a submission deposit operation is pending
|
||||||
|
*/
|
||||||
depositPending?: boolean;
|
depositPending?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ import { of as observableOf } from 'rxjs';
|
|||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { SectionContainerComponent } from './section-container.component';
|
import { SubmissionSectionContainerComponent } from './section-container.component';
|
||||||
import { createTestComponent } from '../../../shared/testing/utils';
|
import { createTestComponent } from '../../../shared/testing/utils';
|
||||||
import { SectionsType } from '../sections-type';
|
import { SectionsType } from '../sections-type';
|
||||||
import { SectionsDirective } from '../sections.directive';
|
import { SectionsDirective } from '../sections.directive';
|
||||||
@@ -41,11 +41,11 @@ const sectionObject: SectionDataObject = {
|
|||||||
sectionType: SectionsType.SubmissionForm
|
sectionType: SectionsType.SubmissionForm
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('SectionContainerComponent test suite', () => {
|
describe('SubmissionSectionContainerComponent test suite', () => {
|
||||||
|
|
||||||
let comp: SectionContainerComponent;
|
let comp: SubmissionSectionContainerComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<SectionContainerComponent>;
|
let fixture: ComponentFixture<SubmissionSectionContainerComponent>;
|
||||||
|
|
||||||
let submissionServiceStub: SubmissionServiceStub;
|
let submissionServiceStub: SubmissionServiceStub;
|
||||||
let sectionsServiceStub: SectionsServiceStub;
|
let sectionsServiceStub: SectionsServiceStub;
|
||||||
@@ -71,14 +71,14 @@ describe('SectionContainerComponent test suite', () => {
|
|||||||
TranslateModule.forRoot()
|
TranslateModule.forRoot()
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
SectionContainerComponent,
|
SubmissionSectionContainerComponent,
|
||||||
SectionsDirective,
|
SectionsDirective,
|
||||||
TestComponent,
|
TestComponent,
|
||||||
], // declare the test component
|
], // declare the test component
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: SectionsService, useClass: SectionsServiceStub },
|
{ provide: SectionsService, useClass: SectionsServiceStub },
|
||||||
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
||||||
SectionContainerComponent
|
SubmissionSectionContainerComponent
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -93,16 +93,16 @@ describe('SectionContainerComponent test suite', () => {
|
|||||||
// synchronous beforeEach
|
// synchronous beforeEach
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
html = `
|
html = `
|
||||||
<ds-submission-form-section-container [collectionId]="collectionId"
|
<ds-submission-section-container [collectionId]="collectionId"
|
||||||
[submissionId]="submissionId"
|
[submissionId]="submissionId"
|
||||||
[sectionData]="object"></ds-submission-form-section-container>`;
|
[sectionData]="object"></ds-submission-section-container>`;
|
||||||
|
|
||||||
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
||||||
testComp = testFixture.componentInstance;
|
testComp = testFixture.componentInstance;
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create SectionContainerComponent', inject([SectionContainerComponent], (app: SectionContainerComponent) => {
|
it('should create SubmissionSectionContainerComponent', inject([SubmissionSectionContainerComponent], (app: SubmissionSectionContainerComponent) => {
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@@ -110,7 +110,7 @@ describe('SectionContainerComponent test suite', () => {
|
|||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
init();
|
init();
|
||||||
fixture = TestBed.createComponent(SectionContainerComponent);
|
fixture = TestBed.createComponent(SubmissionSectionContainerComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
comp.submissionId = submissionId;
|
comp.submissionId = submissionId;
|
||||||
|
@@ -3,29 +3,64 @@ import { Component, Injector, Input, OnInit, ViewChild } from '@angular/core';
|
|||||||
import { SectionsDirective } from '../sections.directive';
|
import { SectionsDirective } from '../sections.directive';
|
||||||
import { SectionDataObject } from '../models/section-data.model';
|
import { SectionDataObject } from '../models/section-data.model';
|
||||||
import { rendersSectionType } from '../sections-decorator';
|
import { rendersSectionType } from '../sections-decorator';
|
||||||
import { SectionsType } from '../sections-type';
|
import { AlertType } from '../../../shared/alert/aletr-type';
|
||||||
import { AlertType } from '../../../shared/alerts/aletrs-type';
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a section that contains the submission license form.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-form-section-container',
|
selector: 'ds-submission-section-container',
|
||||||
templateUrl: './section-container.component.html',
|
templateUrl: './section-container.component.html',
|
||||||
styleUrls: ['./section-container.component.scss']
|
styleUrls: ['./section-container.component.scss']
|
||||||
})
|
})
|
||||||
export class SectionContainerComponent implements OnInit {
|
export class SubmissionSectionContainerComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection id this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() collectionId: string;
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section data
|
||||||
|
* @type {SectionDataObject}
|
||||||
|
*/
|
||||||
@Input() sectionData: SectionDataObject;
|
@Input() sectionData: SectionDataObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
@Input() submissionId: string;
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AlertType enumeration
|
||||||
|
* @type {AlertType}
|
||||||
|
*/
|
||||||
public AlertTypeEnum = AlertType;
|
public AlertTypeEnum = AlertType;
|
||||||
public active = true;
|
|
||||||
public objectInjector: Injector;
|
|
||||||
public sectionComponentType: SectionsType;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injector to inject a section component with the @Input parameters
|
||||||
|
* @type {Injector}
|
||||||
|
*/
|
||||||
|
public objectInjector: Injector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SectionsDirective reference
|
||||||
|
*/
|
||||||
@ViewChild('sectionRef') sectionRef: SectionsDirective;
|
@ViewChild('sectionRef') sectionRef: SectionsDirective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {Injector} injector
|
||||||
|
*/
|
||||||
constructor(private injector: Injector) {
|
constructor(private injector: Injector) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.objectInjector = Injector.create({
|
this.objectInjector = Injector.create({
|
||||||
providers: [
|
providers: [
|
||||||
@@ -37,12 +72,21 @@ export class SectionContainerComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove section from submission form
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the event emitted
|
||||||
|
*/
|
||||||
public removeSection(event) {
|
public removeSection(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.sectionRef.removeSection(this.submissionId, this.sectionData.id);
|
this.sectionRef.removeSection(this.submissionId, this.sectionData.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the correct component based on the section's type
|
||||||
|
*/
|
||||||
getSectionContent(): string {
|
getSectionContent(): string {
|
||||||
return rendersSectionType(this.sectionData.sectionType);
|
return rendersSectionType(this.sectionData.sectionType);
|
||||||
}
|
}
|
||||||
|
@@ -156,7 +156,7 @@ describe('SectionFormOperationsService test suite', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.isPartOfArrayOfGroup(model)).toBeTruthy();
|
expect(service.isPartOfArrayOfGroup(model as any)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when parent element doesn\'t belong to an array group element', () => {
|
it('should return false when parent element doesn\'t belong to an array group element', () => {
|
||||||
@@ -164,7 +164,7 @@ describe('SectionFormOperationsService test suite', () => {
|
|||||||
parent: null
|
parent: null
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.isPartOfArrayOfGroup(model)).toBeFalsy();
|
expect(service.isPartOfArrayOfGroup(model as any)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -21,12 +21,35 @@ import { FormFieldMetadataValueObject } from '../../../shared/form/builder/model
|
|||||||
import { DynamicQualdropModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model';
|
import { DynamicQualdropModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model';
|
||||||
import { DynamicRelationGroupModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
|
import { DynamicRelationGroupModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service handling all form section operations
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SectionFormOperationsService {
|
export class SectionFormOperationsService {
|
||||||
|
|
||||||
constructor(private formBuilder: FormBuilderService, private operationsBuilder: JsonPatchOperationsBuilder) {
|
/**
|
||||||
|
* Initialize service variables
|
||||||
|
*
|
||||||
|
* @param {FormBuilderService} formBuilder
|
||||||
|
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilderService,
|
||||||
|
private operationsBuilder: JsonPatchOperationsBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch properly method based on form operation type
|
||||||
|
*
|
||||||
|
* @param pathCombiner
|
||||||
|
* the [[JsonPatchOperationPathCombiner]] object for the specified operation
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @param previousValue
|
||||||
|
* the [[FormFieldPreviousValueObject]] for the specified operation
|
||||||
|
* @param hasStoredValue
|
||||||
|
* representing if field value related to the specified operation has stored value
|
||||||
|
*/
|
||||||
public dispatchOperationsFromEvent(pathCombiner: JsonPatchOperationPathCombiner,
|
public dispatchOperationsFromEvent(pathCombiner: JsonPatchOperationPathCombiner,
|
||||||
event: DynamicFormControlEvent,
|
event: DynamicFormControlEvent,
|
||||||
previousValue: FormFieldPreviousValueObject,
|
previousValue: FormFieldPreviousValueObject,
|
||||||
@@ -43,6 +66,14 @@ export class SectionFormOperationsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return index if specified field is part of fields array
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @return number
|
||||||
|
* the array index is part of array, zero otherwise
|
||||||
|
*/
|
||||||
public getArrayIndexFromEvent(event: DynamicFormControlEvent): number {
|
public getArrayIndexFromEvent(event: DynamicFormControlEvent): number {
|
||||||
let fieldIndex: number;
|
let fieldIndex: number;
|
||||||
if (isNotEmpty(event)) {
|
if (isNotEmpty(event)) {
|
||||||
@@ -60,7 +91,15 @@ export class SectionFormOperationsService {
|
|||||||
return isNotUndefined(fieldIndex) ? fieldIndex : 0;
|
return isNotUndefined(fieldIndex) ? fieldIndex : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isPartOfArrayOfGroup(model: any): boolean {
|
/**
|
||||||
|
* Check if specified model is part of array of group
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* the [[DynamicFormControlModel]] model
|
||||||
|
* @return boolean
|
||||||
|
* true if is part of array, false otherwise
|
||||||
|
*/
|
||||||
|
public isPartOfArrayOfGroup(model: DynamicFormControlModel): boolean {
|
||||||
return (isNotNull(model.parent)
|
return (isNotNull(model.parent)
|
||||||
&& (model.parent as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP
|
&& (model.parent as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP
|
||||||
&& (model.parent as any).parent
|
&& (model.parent as any).parent
|
||||||
@@ -68,7 +107,15 @@ export class SectionFormOperationsService {
|
|||||||
&& (model.parent as any).parent.context.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY);
|
&& (model.parent as any).parent.context.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getQualdropValueMap(event): Map<string, any> {
|
/**
|
||||||
|
* Return a map for the values of a Qualdrop field
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @return Map<string, any>
|
||||||
|
* the map of values
|
||||||
|
*/
|
||||||
|
public getQualdropValueMap(event: DynamicFormControlEvent): Map<string, any> {
|
||||||
const metadataValueMap = new Map();
|
const metadataValueMap = new Map();
|
||||||
|
|
||||||
const context = this.formBuilder.isQualdropGroup(event.model)
|
const context = this.formBuilder.isQualdropGroup(event.model)
|
||||||
@@ -87,12 +134,28 @@ export class SectionFormOperationsService {
|
|||||||
return metadataValueMap;
|
return metadataValueMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the absolute path for the field interesting in the specified operation
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @return string
|
||||||
|
* the field path
|
||||||
|
*/
|
||||||
public getFieldPathFromEvent(event: DynamicFormControlEvent): string {
|
public getFieldPathFromEvent(event: DynamicFormControlEvent): string {
|
||||||
const fieldIndex = this.getArrayIndexFromEvent(event);
|
const fieldIndex = this.getArrayIndexFromEvent(event);
|
||||||
const fieldId = this.getFieldPathSegmentedFromChangeEvent(event);
|
const fieldId = this.getFieldPathSegmentedFromChangeEvent(event);
|
||||||
return (isNotUndefined(fieldIndex)) ? fieldId + '/' + fieldIndex : fieldId;
|
return (isNotUndefined(fieldIndex)) ? fieldId + '/' + fieldIndex : fieldId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the absolute path for the Qualdrop field interesting in the specified operation
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @return string
|
||||||
|
* the field path
|
||||||
|
*/
|
||||||
public getQualdropItemPathFromEvent(event: DynamicFormControlEvent): string {
|
public getQualdropItemPathFromEvent(event: DynamicFormControlEvent): string {
|
||||||
const fieldIndex = this.getArrayIndexFromEvent(event);
|
const fieldIndex = this.getArrayIndexFromEvent(event);
|
||||||
const metadataValueMap = new Map();
|
const metadataValueMap = new Map();
|
||||||
@@ -117,6 +180,14 @@ export class SectionFormOperationsService {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the segmented path for the field interesting in the specified change operation
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @return string
|
||||||
|
* the field path
|
||||||
|
*/
|
||||||
public getFieldPathSegmentedFromChangeEvent(event: DynamicFormControlEvent): string {
|
public getFieldPathSegmentedFromChangeEvent(event: DynamicFormControlEvent): string {
|
||||||
let fieldId;
|
let fieldId;
|
||||||
if (this.formBuilder.isQualdropGroup(event.model as DynamicFormControlModel)) {
|
if (this.formBuilder.isQualdropGroup(event.model as DynamicFormControlModel)) {
|
||||||
@@ -129,6 +200,14 @@ export class SectionFormOperationsService {
|
|||||||
return fieldId;
|
return fieldId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the value of the field interesting in the specified change operation
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @return any
|
||||||
|
* the field value
|
||||||
|
*/
|
||||||
public getFieldValueFromChangeEvent(event: DynamicFormControlEvent): any {
|
public getFieldValueFromChangeEvent(event: DynamicFormControlEvent): any {
|
||||||
let fieldValue;
|
let fieldValue;
|
||||||
const value = (event.model as any).value;
|
const value = (event.model as any).value;
|
||||||
@@ -142,12 +221,12 @@ export class SectionFormOperationsService {
|
|||||||
if ((event.model as DsDynamicInputModel).hasAuthority) {
|
if ((event.model as DsDynamicInputModel).hasAuthority) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value.forEach((authority, index) => {
|
value.forEach((authority, index) => {
|
||||||
authority = Object.assign(new AuthorityValue(), authority, {language});
|
authority = Object.assign(new AuthorityValue(), authority, { language });
|
||||||
value[index] = authority;
|
value[index] = authority;
|
||||||
});
|
});
|
||||||
fieldValue = value;
|
fieldValue = value;
|
||||||
} else {
|
} else {
|
||||||
fieldValue = Object.assign(new AuthorityValue(), value, {language});
|
fieldValue = Object.assign(new AuthorityValue(), value, { language });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Language without Authority (input, textArea)
|
// Language without Authority (input, textArea)
|
||||||
@@ -162,6 +241,14 @@ export class SectionFormOperationsService {
|
|||||||
return fieldValue;
|
return fieldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a map for the values of an array of field
|
||||||
|
*
|
||||||
|
* @param items
|
||||||
|
* the list of items
|
||||||
|
* @return Map<string, any>
|
||||||
|
* the map of values
|
||||||
|
*/
|
||||||
public getValueMap(items: any[]): Map<string, any> {
|
public getValueMap(items: any[]): Map<string, any> {
|
||||||
const metadataValueMap = new Map();
|
const metadataValueMap = new Map();
|
||||||
|
|
||||||
@@ -177,6 +264,16 @@ export class SectionFormOperationsService {
|
|||||||
return metadataValueMap;
|
return metadataValueMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle form remove operations
|
||||||
|
*
|
||||||
|
* @param pathCombiner
|
||||||
|
* the [[JsonPatchOperationPathCombiner]] object for the specified operation
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @param previousValue
|
||||||
|
* the [[FormFieldPreviousValueObject]] for the specified operation
|
||||||
|
*/
|
||||||
protected dispatchOperationsFromRemoveEvent(pathCombiner: JsonPatchOperationPathCombiner,
|
protected dispatchOperationsFromRemoveEvent(pathCombiner: JsonPatchOperationPathCombiner,
|
||||||
event: DynamicFormControlEvent,
|
event: DynamicFormControlEvent,
|
||||||
previousValue: FormFieldPreviousValueObject): void {
|
previousValue: FormFieldPreviousValueObject): void {
|
||||||
@@ -189,6 +286,18 @@ export class SectionFormOperationsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle form change operations
|
||||||
|
*
|
||||||
|
* @param pathCombiner
|
||||||
|
* the [[JsonPatchOperationPathCombiner]] object for the specified operation
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @param previousValue
|
||||||
|
* the [[FormFieldPreviousValueObject]] for the specified operation
|
||||||
|
* @param hasStoredValue
|
||||||
|
* representing if field value related to the specified operation has stored value
|
||||||
|
*/
|
||||||
protected dispatchOperationsFromChangeEvent(pathCombiner: JsonPatchOperationPathCombiner,
|
protected dispatchOperationsFromChangeEvent(pathCombiner: JsonPatchOperationPathCombiner,
|
||||||
event: DynamicFormControlEvent,
|
event: DynamicFormControlEvent,
|
||||||
previousValue: FormFieldPreviousValueObject,
|
previousValue: FormFieldPreviousValueObject,
|
||||||
@@ -243,6 +352,18 @@ export class SectionFormOperationsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle form operations interesting a field with a map as value
|
||||||
|
*
|
||||||
|
* @param valueMap
|
||||||
|
* map of values
|
||||||
|
* @param pathCombiner
|
||||||
|
* the [[JsonPatchOperationPathCombiner]] object for the specified operation
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] for the specified operation
|
||||||
|
* @param previousValue
|
||||||
|
* the [[FormFieldPreviousValueObject]] for the specified operation
|
||||||
|
*/
|
||||||
protected dispatchOperationsFromMap(valueMap: Map<string, any>,
|
protected dispatchOperationsFromMap(valueMap: Map<string, any>,
|
||||||
pathCombiner: JsonPatchOperationPathCombiner,
|
pathCombiner: JsonPatchOperationPathCombiner,
|
||||||
event: DynamicFormControlEvent,
|
event: DynamicFormControlEvent,
|
||||||
|
@@ -12,7 +12,7 @@ import { SubmissionServiceStub } from '../../../shared/testing/submission-servic
|
|||||||
import { getMockTranslateService } from '../../../shared/mocks/mock-translate.service';
|
import { getMockTranslateService } from '../../../shared/mocks/mock-translate.service';
|
||||||
import { SectionsService } from '../sections.service';
|
import { SectionsService } from '../sections.service';
|
||||||
import { SectionsServiceStub } from '../../../shared/testing/sections-service-stub';
|
import { SectionsServiceStub } from '../../../shared/testing/sections-service-stub';
|
||||||
import { FormSectionComponent } from './section-form.component';
|
import { SubmissionSectionformComponent } from './section-form.component';
|
||||||
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
|
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
|
||||||
import { getMockFormBuilderService } from '../../../shared/mocks/mock-form-builder-service';
|
import { getMockFormBuilderService } from '../../../shared/mocks/mock-form-builder-service';
|
||||||
import { getMockFormOperationsService } from '../../../shared/mocks/mock-form-operations-service';
|
import { getMockFormOperationsService } from '../../../shared/mocks/mock-form-operations-service';
|
||||||
@@ -132,11 +132,11 @@ const dynamicFormControlEvent: DynamicFormControlEvent = {
|
|||||||
type: DynamicFormControlEventType.Change
|
type: DynamicFormControlEventType.Change
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('FormSectionComponent test suite', () => {
|
describe('SubmissionSectionformComponent test suite', () => {
|
||||||
|
|
||||||
let comp: FormSectionComponent;
|
let comp: SubmissionSectionformComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<FormSectionComponent>;
|
let fixture: ComponentFixture<SubmissionSectionformComponent>;
|
||||||
let submissionServiceStub: SubmissionServiceStub;
|
let submissionServiceStub: SubmissionServiceStub;
|
||||||
let sectionsServiceStub: SectionsServiceStub;
|
let sectionsServiceStub: SectionsServiceStub;
|
||||||
let notificationsServiceStub: NotificationsServiceStub;
|
let notificationsServiceStub: NotificationsServiceStub;
|
||||||
@@ -163,7 +163,7 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
FormComponent,
|
FormComponent,
|
||||||
FormSectionComponent,
|
SubmissionSectionformComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -180,7 +180,7 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
{ provide: 'sectionDataProvider', useValue: sectionObject },
|
{ provide: 'sectionDataProvider', useValue: sectionObject },
|
||||||
{ provide: 'submissionIdProvider', useValue: submissionId },
|
{ provide: 'submissionIdProvider', useValue: submissionId },
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
FormSectionComponent
|
SubmissionSectionformComponent
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents().then();
|
}).compileComponents().then();
|
||||||
@@ -203,7 +203,7 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
testFixture.destroy();
|
testFixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create FormSectionComponent', inject([FormSectionComponent], (app: FormSectionComponent) => {
|
it('should create SubmissionSectionformComponent', inject([SubmissionSectionformComponent], (app: SubmissionSectionformComponent) => {
|
||||||
|
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FormSectionComponent);
|
fixture = TestBed.createComponent(SubmissionSectionformComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
submissionServiceStub = TestBed.get(SubmissionService);
|
submissionServiceStub = TestBed.get(SubmissionService);
|
||||||
@@ -236,6 +236,7 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
|
|
||||||
it('should init section properly', () => {
|
it('should init section properly', () => {
|
||||||
const sectionData = {};
|
const sectionData = {};
|
||||||
|
formService.isValid.and.returnValue(observableOf(true));
|
||||||
formConfigService.getConfigByHref.and.returnValue(observableOf(formConfigData));
|
formConfigService.getConfigByHref.and.returnValue(observableOf(formConfigData));
|
||||||
sectionsServiceStub.getSectionData.and.returnValue(observableOf(sectionData));
|
sectionsServiceStub.getSectionData.and.returnValue(observableOf(sectionData));
|
||||||
spyOn(comp, 'initForm');
|
spyOn(comp, 'initForm');
|
||||||
@@ -265,6 +266,7 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
|
|
||||||
it('should set a section Error when init form model fails', () => {
|
it('should set a section Error when init form model fails', () => {
|
||||||
formBuilderService.modelFromConfiguration.and.throwError('test');
|
formBuilderService.modelFromConfiguration.and.throwError('test');
|
||||||
|
translateService.instant.and.returnValue('test');
|
||||||
const sectionData = {};
|
const sectionData = {};
|
||||||
const sectionError: SubmissionSectionError = {
|
const sectionError: SubmissionSectionError = {
|
||||||
message: 'test' + 'Error: test',
|
message: 'test' + 'Error: test',
|
||||||
@@ -317,6 +319,23 @@ describe('FormSectionComponent test suite', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update form error properly', () => {
|
||||||
|
spyOn(comp, 'initForm');
|
||||||
|
spyOn(comp, 'checksForErrors');
|
||||||
|
const sectionData: any = {
|
||||||
|
'dc.title': [new FormFieldMetadataValueObject('test')]
|
||||||
|
};
|
||||||
|
comp.sectionData.data = {};
|
||||||
|
comp.sectionData.errors = [];
|
||||||
|
compAsAny.formData = sectionData;
|
||||||
|
|
||||||
|
comp.updateForm(sectionData, parsedSectionErrors);
|
||||||
|
|
||||||
|
expect(comp.initForm).not.toHaveBeenCalled();
|
||||||
|
expect(comp.checksForErrors).toHaveBeenCalled();
|
||||||
|
expect(comp.sectionData.data).toEqual(sectionData);
|
||||||
|
});
|
||||||
|
|
||||||
it('should update form error properly', () => {
|
it('should update form error properly', () => {
|
||||||
spyOn(comp, 'initForm');
|
spyOn(comp, 'initForm');
|
||||||
spyOn(comp, 'checksForErrors');
|
spyOn(comp, 'checksForErrors');
|
||||||
|
@@ -17,7 +17,6 @@ import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder
|
|||||||
import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model';
|
import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model';
|
||||||
import { SubmissionSectionError, SubmissionSectionObject } from '../../objects/submission-objects.reducer';
|
import { SubmissionSectionError, SubmissionSectionObject } from '../../objects/submission-objects.reducer';
|
||||||
import { FormFieldPreviousValueObject } from '../../../shared/form/builder/models/form-field-previous-value-object';
|
import { FormFieldPreviousValueObject } from '../../../shared/form/builder/models/form-field-previous-value-object';
|
||||||
import { WorkspaceitemSectionDataType } from '../../../core/submission/models/workspaceitem-sections.model';
|
|
||||||
import { GLOBAL_CONFIG } from '../../../../config';
|
import { GLOBAL_CONFIG } from '../../../../config';
|
||||||
import { GlobalConfig } from '../../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../../config/global-config.interface';
|
||||||
import { SectionDataObject } from '../models/section-data.model';
|
import { SectionDataObject } from '../models/section-data.model';
|
||||||
@@ -28,28 +27,95 @@ import { SectionFormOperationsService } from './section-form-operations.service'
|
|||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { SectionsService } from '../sections.service';
|
import { SectionsService } from '../sections.service';
|
||||||
import { difference } from '../../../shared/object.util';
|
import { difference } from '../../../shared/object.util';
|
||||||
|
import { WorkspaceitemSectionFormObject } from '../../../core/submission/models/workspaceitem-section-form.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a section that contains a Form.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-section-form',
|
selector: 'ds-submission-section-form',
|
||||||
styleUrls: ['./section-form.component.scss'],
|
styleUrls: ['./section-form.component.scss'],
|
||||||
templateUrl: './section-form.component.html',
|
templateUrl: './section-form.component.html',
|
||||||
})
|
})
|
||||||
@renderSectionFor(SectionsType.SubmissionForm)
|
@renderSectionFor(SectionsType.SubmissionForm)
|
||||||
export class FormSectionComponent extends SectionModelComponent {
|
export class SubmissionSectionformComponent extends SectionModelComponent {
|
||||||
|
|
||||||
public formId;
|
/**
|
||||||
|
* The form id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
public formId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form model
|
||||||
|
* @type {DynamicFormControlModel[]}
|
||||||
|
*/
|
||||||
public formModel: DynamicFormControlModel[];
|
public formModel: DynamicFormControlModel[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is updating
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
public isUpdating = false;
|
public isUpdating = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is loading
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
public isLoading = true;
|
public isLoading = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form config
|
||||||
|
* @type {SubmissionFormsModel}
|
||||||
|
*/
|
||||||
protected formConfig: SubmissionFormsModel;
|
protected formConfig: SubmissionFormsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form data
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
protected formData: any = Object.create({});
|
protected formData: any = Object.create({});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [JsonPatchOperationPathCombiner] object
|
||||||
|
* @type {JsonPatchOperationPathCombiner}
|
||||||
|
*/
|
||||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [FormFieldPreviousValueObject] object
|
||||||
|
* @type {FormFieldPreviousValueObject}
|
||||||
|
*/
|
||||||
protected previousValue: FormFieldPreviousValueObject = new FormFieldPreviousValueObject();
|
protected previousValue: FormFieldPreviousValueObject = new FormFieldPreviousValueObject();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of Subscription
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
protected subs: Subscription[] = [];
|
protected subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FormComponent reference
|
||||||
|
*/
|
||||||
@ViewChild('formRef') private formRef: FormComponent;
|
@ViewChild('formRef') private formRef: FormComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} cdr
|
||||||
|
* @param {FormBuilderService} formBuilderService
|
||||||
|
* @param {SectionFormOperationsService} formOperationsService
|
||||||
|
* @param {FormService} formService
|
||||||
|
* @param {SubmissionFormsConfigService} formConfigService
|
||||||
|
* @param {NotificationsService} notificationsService
|
||||||
|
* @param {SectionsService} sectionService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {TranslateService} translate
|
||||||
|
* @param {GlobalConfig} EnvConfig
|
||||||
|
* @param {string} injectedCollectionId
|
||||||
|
* @param {SectionDataObject} injectedSectionData
|
||||||
|
* @param {string} injectedSubmissionId
|
||||||
|
*/
|
||||||
constructor(protected cdr: ChangeDetectorRef,
|
constructor(protected cdr: ChangeDetectorRef,
|
||||||
protected formBuilderService: FormBuilderService,
|
protected formBuilderService: FormBuilderService,
|
||||||
protected formOperationsService: SectionFormOperationsService,
|
protected formOperationsService: SectionFormOperationsService,
|
||||||
@@ -66,6 +132,9 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
super(injectedCollectionId, injectedSectionData, injectedSubmissionId);
|
super(injectedCollectionId, injectedSectionData, injectedSubmissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables and retrieve form configuration
|
||||||
|
*/
|
||||||
onSectionInit() {
|
onSectionInit() {
|
||||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
||||||
this.formId = this.formService.getUniqueId(this.sectionData.id);
|
this.formId = this.formService.getUniqueId(this.sectionData.id);
|
||||||
@@ -75,30 +144,45 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
tap((config: SubmissionFormsModel) => this.formConfig = config),
|
tap((config: SubmissionFormsModel) => this.formConfig = config),
|
||||||
flatMap(() => this.sectionService.getSectionData(this.submissionId, this.sectionData.id)),
|
flatMap(() => this.sectionService.getSectionData(this.submissionId, this.sectionData.id)),
|
||||||
take(1))
|
take(1))
|
||||||
.subscribe((sectionData: WorkspaceitemSectionDataType) => {
|
.subscribe((sectionData: WorkspaceitemSectionFormObject) => {
|
||||||
if (isUndefined(this.formModel)) {
|
if (isUndefined(this.formModel)) {
|
||||||
this.sectionData.errors = [];
|
this.sectionData.errors = [];
|
||||||
// Is the first loading so init form
|
// Is the first loading so init form
|
||||||
this.initForm(sectionData);
|
this.initForm(sectionData);
|
||||||
this.sectionData.data = sectionData;
|
this.sectionData.data = sectionData;
|
||||||
this.subscriptions();
|
this.subscriptions();
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions
|
||||||
|
*/
|
||||||
onSectionDestroy() {
|
onSectionDestroy() {
|
||||||
this.subs
|
this.subs
|
||||||
.filter((subscription) => hasValue(subscription))
|
.filter((subscription) => hasValue(subscription))
|
||||||
.forEach((subscription) => subscription.unsubscribe());
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get section status
|
||||||
|
*
|
||||||
|
* @return Observable<boolean>
|
||||||
|
* the section status
|
||||||
|
*/
|
||||||
protected getSectionStatus(): Observable<boolean> {
|
protected getSectionStatus(): Observable<boolean> {
|
||||||
return this.formService.isValid(this.formId);
|
return this.formService.isValid(this.formId);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMetadataEnrichment(sectionData): boolean {
|
/**
|
||||||
|
* Check if the section data has been enriched by the server
|
||||||
|
*
|
||||||
|
* @param sectionData
|
||||||
|
* the section data retrieved from the server
|
||||||
|
*/
|
||||||
|
hasMetadataEnrichment(sectionData: WorkspaceitemSectionFormObject): boolean {
|
||||||
const diffResult = [];
|
const diffResult = [];
|
||||||
|
|
||||||
// compare current form data state with section data retrieved from store
|
// compare current form data state with section data retrieved from store
|
||||||
@@ -116,7 +200,13 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
return isNotEmpty(diffResult);
|
return isNotEmpty(diffResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
initForm(sectionData: WorkspaceitemSectionDataType) {
|
/**
|
||||||
|
* Initialize form model
|
||||||
|
*
|
||||||
|
* @param sectionData
|
||||||
|
* the section data retrieved from the server
|
||||||
|
*/
|
||||||
|
initForm(sectionData: WorkspaceitemSectionFormObject): void {
|
||||||
try {
|
try {
|
||||||
this.formModel = this.formBuilderService.modelFromConfiguration(
|
this.formModel = this.formBuilderService.modelFromConfiguration(
|
||||||
this.formConfig,
|
this.formConfig,
|
||||||
@@ -124,28 +214,32 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
sectionData,
|
sectionData,
|
||||||
this.submissionService.getSubmissionScope());
|
this.submissionService.getSubmissionScope());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.translate.get('error.submission.sections.init-form-error')
|
const msg: string = this.translate.instant('error.submission.sections.init-form-error') + e.toString();
|
||||||
.subscribe((msg) => {
|
const sectionError: SubmissionSectionError = {
|
||||||
const sectionError: SubmissionSectionError = {
|
message: msg,
|
||||||
message: msg + e.toString(),
|
path: '/sections/' + this.sectionData.id
|
||||||
path: '/sections/' + this.sectionData.id
|
};
|
||||||
};
|
this.sectionService.setSectionError(this.submissionId, this.sectionData.id, sectionError);
|
||||||
this.sectionService.setSectionError(this.submissionId, this.sectionData.id, sectionError);
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateForm(sectionData: WorkspaceitemSectionDataType, errors: SubmissionSectionError[]) {
|
/**
|
||||||
|
* Update form model
|
||||||
|
*
|
||||||
|
* @param sectionData
|
||||||
|
* the section data retrieved from the server
|
||||||
|
* @param errors
|
||||||
|
* the section errors retrieved from the server
|
||||||
|
*/
|
||||||
|
updateForm(sectionData: WorkspaceitemSectionFormObject, errors: SubmissionSectionError[]): void {
|
||||||
|
|
||||||
if (isNotEmpty(sectionData) && !isEqual(sectionData, this.sectionData.data)) {
|
if (isNotEmpty(sectionData) && !isEqual(sectionData, this.sectionData.data)) {
|
||||||
this.sectionData.data = sectionData;
|
this.sectionData.data = sectionData;
|
||||||
if (this.hasMetadataEnrichment(sectionData)) {
|
if (this.hasMetadataEnrichment(sectionData)) {
|
||||||
this.translate.get('submission.sections.general.metadata-extracted', { sectionId: this.sectionData.id })
|
const msg = this.translate.instant(
|
||||||
.pipe(take(1))
|
'submission.sections.general.metadata-extracted',
|
||||||
.subscribe((m) => {
|
{ sectionId: this.sectionData.id });
|
||||||
this.notificationsService.info(null, m, null, true);
|
this.notificationsService.info(null, msg, null, true);
|
||||||
});
|
|
||||||
this.isUpdating = true;
|
this.isUpdating = true;
|
||||||
this.formModel = null;
|
this.formModel = null;
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
@@ -153,6 +247,8 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
this.checksForErrors(errors);
|
this.checksForErrors(errors);
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
|
} else if (isNotEmpty(errors) || isNotEmpty(this.sectionData.errors)) {
|
||||||
|
this.checksForErrors(errors);
|
||||||
}
|
}
|
||||||
} else if (isNotEmpty(errors) || isNotEmpty(this.sectionData.errors)) {
|
} else if (isNotEmpty(errors) || isNotEmpty(this.sectionData.errors)) {
|
||||||
this.checksForErrors(errors);
|
this.checksForErrors(errors);
|
||||||
@@ -160,7 +256,13 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checksForErrors(errors: SubmissionSectionError[]) {
|
/**
|
||||||
|
* Check if there are form validation error retrieved from server
|
||||||
|
*
|
||||||
|
* @param errors
|
||||||
|
* the section errors retrieved from the server
|
||||||
|
*/
|
||||||
|
checksForErrors(errors: SubmissionSectionError[]): void {
|
||||||
this.formService.isFormInitialized(this.formId).pipe(
|
this.formService.isFormInitialized(this.formId).pipe(
|
||||||
find((status: boolean) => status === true && !this.isUpdating))
|
find((status: boolean) => status === true && !this.isUpdating))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
@@ -170,9 +272,11 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions() {
|
/**
|
||||||
|
* Initialize all subscriptions
|
||||||
|
*/
|
||||||
|
subscriptions(): void {
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to form's data
|
* Subscribe to form's data
|
||||||
*/
|
*/
|
||||||
@@ -181,6 +285,7 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
.subscribe((formData) => {
|
.subscribe((formData) => {
|
||||||
this.formData = formData;
|
this.formData = formData;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to section state
|
* Subscribe to section state
|
||||||
*/
|
*/
|
||||||
@@ -190,12 +295,19 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
}),
|
}),
|
||||||
distinctUntilChanged())
|
distinctUntilChanged())
|
||||||
.subscribe((sectionState: SubmissionSectionObject) => {
|
.subscribe((sectionState: SubmissionSectionObject) => {
|
||||||
this.updateForm(sectionState.data, sectionState.errors);
|
this.updateForm(sectionState.data as WorkspaceitemSectionFormObject, sectionState.errors);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(event: DynamicFormControlEvent) {
|
/**
|
||||||
|
* Method called when a form dfChange event is fired.
|
||||||
|
* Dispatch form operations based on changes.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] emitted
|
||||||
|
*/
|
||||||
|
onChange(event: DynamicFormControlEvent): void {
|
||||||
this.formOperationsService.dispatchOperationsFromEvent(
|
this.formOperationsService.dispatchOperationsFromEvent(
|
||||||
this.pathCombiner,
|
this.pathCombiner,
|
||||||
event,
|
event,
|
||||||
@@ -209,7 +321,14 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocus(event: DynamicFormControlEvent) {
|
/**
|
||||||
|
* Method called when a form dfFocus event is fired.
|
||||||
|
* Initialize [FormFieldPreviousValueObject] instance.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] emitted
|
||||||
|
*/
|
||||||
|
onFocus(event: DynamicFormControlEvent): void {
|
||||||
const value = this.formOperationsService.getFieldValueFromChangeEvent(event);
|
const value = this.formOperationsService.getFieldValueFromChangeEvent(event);
|
||||||
const path = this.formBuilderService.getPath(event.model);
|
const path = this.formBuilderService.getPath(event.model);
|
||||||
if (this.formBuilderService.hasMappedGroupValue(event.model)) {
|
if (this.formBuilderService.hasMappedGroupValue(event.model)) {
|
||||||
@@ -221,7 +340,14 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(event: DynamicFormControlEvent) {
|
/**
|
||||||
|
* Method called when a form remove event is fired.
|
||||||
|
* Dispatch form operations based on changes.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the [[DynamicFormControlEvent]] emitted
|
||||||
|
*/
|
||||||
|
onRemove(event: DynamicFormControlEvent): void {
|
||||||
this.formOperationsService.dispatchOperationsFromEvent(
|
this.formOperationsService.dispatchOperationsFromEvent(
|
||||||
this.pathCombiner,
|
this.pathCombiner,
|
||||||
event,
|
event,
|
||||||
@@ -229,7 +355,15 @@ export class FormSectionComponent extends SectionModelComponent {
|
|||||||
this.hasStoredValue(this.formBuilderService.getId(event.model), this.formOperationsService.getArrayIndexFromEvent(event)));
|
this.hasStoredValue(this.formBuilderService.getId(event.model), this.formOperationsService.getArrayIndexFromEvent(event)));
|
||||||
}
|
}
|
||||||
|
|
||||||
hasStoredValue(fieldId, index) {
|
/**
|
||||||
|
* Check if the specified form field has already a value stored
|
||||||
|
*
|
||||||
|
* @param fieldId
|
||||||
|
* the section data retrieved from the serverù
|
||||||
|
* @param index
|
||||||
|
* the section data retrieved from the server
|
||||||
|
*/
|
||||||
|
hasStoredValue(fieldId, index): boolean {
|
||||||
if (isNotEmpty(this.sectionData.data)) {
|
if (isNotEmpty(this.sectionData.data)) {
|
||||||
return this.sectionData.data.hasOwnProperty(fieldId) && isNotEmpty(this.sectionData.data[fieldId][index]);
|
return this.sectionData.data.hasOwnProperty(fieldId) && isNotEmpty(this.sectionData.data[fieldId][index]);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -29,7 +29,7 @@ import {
|
|||||||
} from '../../../shared/mocks/mock-submission';
|
} from '../../../shared/mocks/mock-submission';
|
||||||
import { FormComponent } from '../../../shared/form/form.component';
|
import { FormComponent } from '../../../shared/form/form.component';
|
||||||
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 { LicenseSectionComponent } from './section-license.component';
|
import { SubmissionSectionLicenseComponent } from './section-license.component';
|
||||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
||||||
import { SectionFormOperationsService } from '../form/section-form-operations.service';
|
import { SectionFormOperationsService } from '../form/section-form-operations.service';
|
||||||
@@ -78,11 +78,11 @@ const dynamicFormControlEvent: DynamicFormControlEvent = {
|
|||||||
type: DynamicFormControlEventType.Change
|
type: DynamicFormControlEventType.Change
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('LicenseSectionComponent test suite', () => {
|
describe('SubmissionSectionLicenseComponent test suite', () => {
|
||||||
|
|
||||||
let comp: LicenseSectionComponent;
|
let comp: SubmissionSectionLicenseComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<LicenseSectionComponent>;
|
let fixture: ComponentFixture<SubmissionSectionLicenseComponent>;
|
||||||
let submissionServiceStub: SubmissionServiceStub;
|
let submissionServiceStub: SubmissionServiceStub;
|
||||||
let sectionsServiceStub: SectionsServiceStub;
|
let sectionsServiceStub: SectionsServiceStub;
|
||||||
let formService: any;
|
let formService: any;
|
||||||
@@ -124,7 +124,7 @@ describe('LicenseSectionComponent test suite', () => {
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
FormComponent,
|
FormComponent,
|
||||||
LicenseSectionComponent,
|
SubmissionSectionLicenseComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -141,7 +141,7 @@ describe('LicenseSectionComponent test suite', () => {
|
|||||||
{ provide: 'submissionIdProvider', useValue: submissionId },
|
{ provide: 'submissionIdProvider', useValue: submissionId },
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
FormBuilderService,
|
FormBuilderService,
|
||||||
LicenseSectionComponent
|
SubmissionSectionLicenseComponent
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents().then();
|
}).compileComponents().then();
|
||||||
@@ -164,7 +164,7 @@ describe('LicenseSectionComponent test suite', () => {
|
|||||||
testFixture.destroy();
|
testFixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create LicenseSectionComponent', inject([LicenseSectionComponent], (app: LicenseSectionComponent) => {
|
it('should create SubmissionSectionLicenseComponent', inject([SubmissionSectionLicenseComponent], (app: SubmissionSectionLicenseComponent) => {
|
||||||
|
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ describe('LicenseSectionComponent test suite', () => {
|
|||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(LicenseSectionComponent);
|
fixture = TestBed.createComponent(SubmissionSectionLicenseComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
submissionServiceStub = TestBed.get(SubmissionService);
|
submissionServiceStub = TestBed.get(SubmissionService);
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectorRef, Component, Inject, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, Inject, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, find, flatMap, map, startWith, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, find, flatMap, map, startWith, take } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
DynamicCheckboxModel,
|
DynamicCheckboxModel,
|
||||||
DynamicFormControlEvent,
|
DynamicFormControlEvent,
|
||||||
@@ -29,25 +29,79 @@ import { SectionsService } from '../sections.service';
|
|||||||
import { SectionFormOperationsService } from '../form/section-form-operations.service';
|
import { SectionFormOperationsService } from '../form/section-form-operations.service';
|
||||||
import { FormComponent } from '../../../shared/form/form.component';
|
import { FormComponent } from '../../../shared/form/form.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a section that contains the submission license form.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-section-license',
|
selector: 'ds-submission-section-license',
|
||||||
styleUrls: ['./section-license.component.scss'],
|
styleUrls: ['./section-license.component.scss'],
|
||||||
templateUrl: './section-license.component.html',
|
templateUrl: './section-license.component.html',
|
||||||
})
|
})
|
||||||
@renderSectionFor(SectionsType.License)
|
@renderSectionFor(SectionsType.License)
|
||||||
export class LicenseSectionComponent extends SectionModelComponent {
|
export class SubmissionSectionLicenseComponent extends SectionModelComponent {
|
||||||
|
|
||||||
public formId;
|
/**
|
||||||
|
* The form id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
public formId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form model
|
||||||
|
* @type {DynamicFormControlModel[]}
|
||||||
|
*/
|
||||||
public formModel: DynamicFormControlModel[];
|
public formModel: DynamicFormControlModel[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [[DynamicFormLayout]] object
|
||||||
|
* @type {DynamicFormLayout}
|
||||||
|
*/
|
||||||
public formLayout: DynamicFormLayout = SECTION_LICENSE_FORM_LAYOUT;
|
public formLayout: DynamicFormLayout = SECTION_LICENSE_FORM_LAYOUT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if to show form submit and cancel buttons
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
public displaySubmit = false;
|
public displaySubmit = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission license text
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
public licenseText$: Observable<string>;
|
public licenseText$: Observable<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [[JsonPatchOperationPathCombiner]] object
|
||||||
|
* @type {JsonPatchOperationPathCombiner}
|
||||||
|
*/
|
||||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
protected subs: Subscription[] = [];
|
protected subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FormComponent reference
|
||||||
|
*/
|
||||||
@ViewChild('formRef') private formRef: FormComponent;
|
@ViewChild('formRef') private formRef: FormComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} changeDetectorRef
|
||||||
|
* @param {CollectionDataService} collectionDataService
|
||||||
|
* @param {FormBuilderService} formBuilderService
|
||||||
|
* @param {SectionFormOperationsService} formOperationsService
|
||||||
|
* @param {FormService} formService
|
||||||
|
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||||
|
* @param {SectionsService} sectionService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {string} injectedCollectionId
|
||||||
|
* @param {SectionDataObject} injectedSectionData
|
||||||
|
* @param {string} injectedSubmissionId
|
||||||
|
*/
|
||||||
constructor(protected changeDetectorRef: ChangeDetectorRef,
|
constructor(protected changeDetectorRef: ChangeDetectorRef,
|
||||||
protected collectionDataService: CollectionDataService,
|
protected collectionDataService: CollectionDataService,
|
||||||
protected formBuilderService: FormBuilderService,
|
protected formBuilderService: FormBuilderService,
|
||||||
@@ -62,6 +116,9 @@ export class LicenseSectionComponent extends SectionModelComponent {
|
|||||||
super(injectedCollectionId, injectedSectionData, injectedSubmissionId);
|
super(injectedCollectionId, injectedSectionData, injectedSubmissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables and retrieve submission license
|
||||||
|
*/
|
||||||
onSectionInit() {
|
onSectionInit() {
|
||||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
||||||
this.formId = this.formService.getUniqueId(this.sectionData.id);
|
this.formId = this.formService.getUniqueId(this.sectionData.id);
|
||||||
@@ -126,6 +183,12 @@ export class LicenseSectionComponent extends SectionModelComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get section status
|
||||||
|
*
|
||||||
|
* @return Observable<boolean>
|
||||||
|
* the section status
|
||||||
|
*/
|
||||||
protected getSectionStatus(): Observable<boolean> {
|
protected getSectionStatus(): Observable<boolean> {
|
||||||
const model = this.formBuilderService.findById('granted', this.formModel);
|
const model = this.formBuilderService.findById('granted', this.formModel);
|
||||||
return (model as DynamicCheckboxModel).valueUpdates.pipe(
|
return (model as DynamicCheckboxModel).valueUpdates.pipe(
|
||||||
@@ -133,6 +196,10 @@ export class LicenseSectionComponent extends SectionModelComponent {
|
|||||||
startWith((model as DynamicCheckboxModel).value));
|
startWith((model as DynamicCheckboxModel).value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called when a form dfChange event is fired.
|
||||||
|
* Dispatch form operations based on changes.
|
||||||
|
*/
|
||||||
onChange(event: DynamicFormControlEvent) {
|
onChange(event: DynamicFormControlEvent) {
|
||||||
const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event);
|
const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event);
|
||||||
const value = this.formOperationsService.getFieldValueFromChangeEvent(event);
|
const value = this.formOperationsService.getFieldValueFromChangeEvent(event);
|
||||||
@@ -145,6 +212,9 @@ export class LicenseSectionComponent extends SectionModelComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions
|
||||||
|
*/
|
||||||
onSectionDestroy() {
|
onSectionDestroy() {
|
||||||
this.subs
|
this.subs
|
||||||
.filter((subscription) => hasValue(subscription))
|
.filter((subscription) => hasValue(subscription))
|
||||||
|
@@ -2,14 +2,48 @@ import { SubmissionSectionError } from '../../objects/submission-objects.reducer
|
|||||||
import { WorkspaceitemSectionDataType } from '../../../core/submission/models/workspaceitem-sections.model';
|
import { WorkspaceitemSectionDataType } from '../../../core/submission/models/workspaceitem-sections.model';
|
||||||
import { SectionsType } from '../sections-type';
|
import { SectionsType } from '../sections-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to represent section model
|
||||||
|
*/
|
||||||
export interface SectionDataObject {
|
export interface SectionDataObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section configuration url
|
||||||
|
*/
|
||||||
config: string;
|
config: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section data object
|
||||||
|
*/
|
||||||
data: WorkspaceitemSectionDataType;
|
data: WorkspaceitemSectionDataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of the section errors
|
||||||
|
*/
|
||||||
errors: SubmissionSectionError[];
|
errors: SubmissionSectionError[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section header
|
||||||
|
*/
|
||||||
header: string;
|
header: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section id
|
||||||
|
*/
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is mandatory
|
||||||
|
*/
|
||||||
mandatory: boolean;
|
mandatory: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section type
|
||||||
|
*/
|
||||||
sectionType: SectionsType;
|
sectionType: SectionsType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eventually additional fields
|
||||||
|
*/
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
@@ -16,12 +16,44 @@ export interface SectionDataModel {
|
|||||||
*/
|
*/
|
||||||
export abstract class SectionModelComponent implements OnDestroy, OnInit, SectionDataModel {
|
export abstract class SectionModelComponent implements OnDestroy, OnInit, SectionDataModel {
|
||||||
protected abstract sectionService: SectionsService;
|
protected abstract sectionService: SectionsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection id this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section data
|
||||||
|
* @type {SectionDataObject}
|
||||||
|
*/
|
||||||
sectionData: SectionDataObject;
|
sectionData: SectionDataObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
submissionId: string;
|
submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if this section is valid
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
protected valid: boolean;
|
protected valid: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Subscription to section status observable
|
||||||
|
* @type {Subscription}
|
||||||
|
*/
|
||||||
private sectionStatusSub: Subscription;
|
private sectionStatusSub: Subscription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {string} injectedCollectionId
|
||||||
|
* @param {SectionDataObject} injectedSectionData
|
||||||
|
* @param {string} injectedSubmissionId
|
||||||
|
*/
|
||||||
public constructor(@Inject('collectionIdProvider') public injectedCollectionId: string,
|
public constructor(@Inject('collectionIdProvider') public injectedCollectionId: string,
|
||||||
@Inject('sectionDataProvider') public injectedSectionData: SectionDataObject,
|
@Inject('sectionDataProvider') public injectedSectionData: SectionDataObject,
|
||||||
@Inject('submissionIdProvider') public injectedSubmissionId: string) {
|
@Inject('submissionIdProvider') public injectedSubmissionId: string) {
|
||||||
@@ -30,15 +62,43 @@ export abstract class SectionModelComponent implements OnDestroy, OnInit, Sectio
|
|||||||
this.submissionId = injectedSubmissionId;
|
this.submissionId = injectedSubmissionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call abstract methods on component init
|
||||||
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.onSectionInit();
|
this.onSectionInit();
|
||||||
this.updateSectionStatus();
|
this.updateSectionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method to implement to get section status
|
||||||
|
*
|
||||||
|
* @return Observable<boolean>
|
||||||
|
* the section status
|
||||||
|
*/
|
||||||
protected abstract getSectionStatus(): Observable<boolean>;
|
protected abstract getSectionStatus(): Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method called on component init.
|
||||||
|
* It must be used instead of ngOnInit on the component that extend this abstract class
|
||||||
|
*
|
||||||
|
* @return Observable<boolean>
|
||||||
|
* the section status
|
||||||
|
*/
|
||||||
protected abstract onSectionInit(): void;
|
protected abstract onSectionInit(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method called on component destroy.
|
||||||
|
* It must be used instead of ngOnDestroy on the component that extend this abstract class
|
||||||
|
*
|
||||||
|
* @return Observable<boolean>
|
||||||
|
* the section status
|
||||||
|
*/
|
||||||
protected abstract onSectionDestroy(): void;
|
protected abstract onSectionDestroy(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to section status
|
||||||
|
*/
|
||||||
protected updateSectionStatus(): void {
|
protected updateSectionStatus(): void {
|
||||||
this.sectionStatusSub = this.getSectionStatus().pipe(
|
this.sectionStatusSub = this.getSectionStatus().pipe(
|
||||||
filter((sectionStatus: boolean) => isNotUndefined(sectionStatus)),
|
filter((sectionStatus: boolean) => isNotUndefined(sectionStatus)),
|
||||||
@@ -48,6 +108,9 @@ export abstract class SectionModelComponent implements OnDestroy, OnInit, Sectio
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions and Call abstract methods on component destroy
|
||||||
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (hasValue(this.sectionStatusSub)) {
|
if (hasValue(this.sectionStatusSub)) {
|
||||||
this.sectionStatusSub.unsubscribe();
|
this.sectionStatusSub.unsubscribe();
|
||||||
|
@@ -10,28 +10,90 @@ import { SubmissionSectionError, SubmissionSectionObject } from '../objects/subm
|
|||||||
import parseSectionErrorPaths, { SectionErrorPath } from '../utils/parseSectionErrorPaths';
|
import parseSectionErrorPaths, { SectionErrorPath } from '../utils/parseSectionErrorPaths';
|
||||||
import { SubmissionService } from '../submission.service';
|
import { SubmissionService } from '../submission.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive for handling generic section functionality
|
||||||
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[dsSection]',
|
selector: '[dsSection]',
|
||||||
exportAs: 'sectionRef'
|
exportAs: 'sectionRef'
|
||||||
})
|
})
|
||||||
export class SectionsDirective implements OnDestroy, OnInit {
|
export class SectionsDirective implements OnDestroy, OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if section is mandatory
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
@Input() mandatory = true;
|
@Input() mandatory = true;
|
||||||
@Input() sectionId;
|
|
||||||
@Input() submissionId;
|
/**
|
||||||
|
* The section id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() sectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of generic errors related to the section
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
public genericSectionErrors: string[] = [];
|
public genericSectionErrors: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of all errors related to the element belonging to this section
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
public allSectionErrors: string[] = [];
|
public allSectionErrors: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if section is active
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private active = true;
|
private active = true;
|
||||||
private animation = !this.mandatory;
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if section is enabled
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private enabled: Observable<boolean>;
|
private enabled: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing the panel collapsible state: opened (true) or closed (false)
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private sectionState = this.mandatory;
|
private sectionState = this.mandatory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if section is valid
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
private valid: Observable<boolean>;
|
private valid: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} changeDetectorRef
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {SectionsService} sectionService
|
||||||
|
*/
|
||||||
constructor(private changeDetectorRef: ChangeDetectorRef,
|
constructor(private changeDetectorRef: ChangeDetectorRef,
|
||||||
private submissionService: SubmissionService,
|
private submissionService: SubmissionService,
|
||||||
private sectionService: SectionsService) {
|
private sectionService: SectionsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.valid = this.sectionService.isSectionValid(this.submissionId, this.sectionId).pipe(
|
this.valid = this.sectionService.isSectionValid(this.submissionId, this.sectionId).pipe(
|
||||||
map((valid: boolean) => {
|
map((valid: boolean) => {
|
||||||
@@ -78,67 +140,145 @@ export class SectionsDirective implements OnDestroy, OnInit {
|
|||||||
this.enabled = this.sectionService.isSectionEnabled(this.submissionId, this.sectionId);
|
this.enabled = this.sectionService.isSectionEnabled(this.submissionId, this.sectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions
|
||||||
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.subs
|
this.subs
|
||||||
.filter((subscription) => hasValue(subscription))
|
.filter((subscription) => hasValue(subscription))
|
||||||
.forEach((subscription) => subscription.unsubscribe());
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change section state
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the event emitted
|
||||||
|
*/
|
||||||
public sectionChange(event) {
|
public sectionChange(event) {
|
||||||
this.sectionState = event.nextState;
|
this.sectionState = event.nextState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isOpen() {
|
/**
|
||||||
|
* Check if section panel is open
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* Returns true when section panel is open
|
||||||
|
*/
|
||||||
|
public isOpen(): boolean {
|
||||||
return this.sectionState;
|
return this.sectionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isMandatory() {
|
/**
|
||||||
|
* Check if section is mandatory
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* Returns true when section is mandatory
|
||||||
|
*/
|
||||||
|
public isMandatory(): boolean {
|
||||||
return this.mandatory;
|
return this.mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isAnimationsActive() {
|
/**
|
||||||
return this.animation;
|
* Check if section panel is active
|
||||||
}
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* Returns true when section panel is active
|
||||||
|
*/
|
||||||
public isSectionActive(): boolean {
|
public isSectionActive(): boolean {
|
||||||
return this.active;
|
return this.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if section is enabled
|
||||||
|
*
|
||||||
|
* @returns {Observable<boolean>}
|
||||||
|
* Emits true whenever section is enabled
|
||||||
|
*/
|
||||||
public isEnabled(): Observable<boolean> {
|
public isEnabled(): Observable<boolean> {
|
||||||
return this.enabled;
|
return this.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if section is valid
|
||||||
|
*
|
||||||
|
* @returns {Observable<boolean>}
|
||||||
|
* Emits true whenever section is valid
|
||||||
|
*/
|
||||||
public isValid(): Observable<boolean> {
|
public isValid(): Observable<boolean> {
|
||||||
return this.valid;
|
return this.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeSection(submissionId, sectionId) {
|
/**
|
||||||
|
* Remove section panel from submission form
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* the submission id
|
||||||
|
* @param sectionId
|
||||||
|
* the section id
|
||||||
|
* @returns {Observable<boolean>}
|
||||||
|
* Emits true whenever section is valid
|
||||||
|
*/
|
||||||
|
public removeSection(submissionId: string, sectionId: string) {
|
||||||
this.sectionService.removeSection(submissionId, sectionId)
|
this.sectionService.removeSection(submissionId, sectionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasGenericErrors() {
|
/**
|
||||||
|
* Check if section has only generic errors
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* Returns true when section has only generic errors
|
||||||
|
*/
|
||||||
|
public hasGenericErrors(): boolean {
|
||||||
return this.genericSectionErrors && this.genericSectionErrors.length > 0
|
return this.genericSectionErrors && this.genericSectionErrors.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasErrors() {
|
/**
|
||||||
|
* Check if section has errors
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* Returns true when section has errors
|
||||||
|
*/
|
||||||
|
public hasErrors(): boolean {
|
||||||
return (this.genericSectionErrors && this.genericSectionErrors.length > 0) ||
|
return (this.genericSectionErrors && this.genericSectionErrors.length > 0) ||
|
||||||
(this.allSectionErrors && this.allSectionErrors.length > 0)
|
(this.allSectionErrors && this.allSectionErrors.length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getErrors() {
|
/**
|
||||||
|
* Return section errors
|
||||||
|
*
|
||||||
|
* @returns {Array}
|
||||||
|
* Returns section errors list
|
||||||
|
*/
|
||||||
|
public getErrors(): string[] {
|
||||||
return this.genericSectionErrors;
|
return this.genericSectionErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setFocus(event) {
|
/**
|
||||||
|
* Set form focus to this section panel
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event emitted
|
||||||
|
*/
|
||||||
|
public setFocus(event): void {
|
||||||
if (!this.active) {
|
if (!this.active) {
|
||||||
this.submissionService.setActiveSection(this.submissionId, this.sectionId);
|
this.submissionService.setActiveSection(this.submissionId, this.sectionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeError(index) {
|
/**
|
||||||
|
* Remove error from list
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* The error array key
|
||||||
|
*/
|
||||||
|
public removeError(index): void {
|
||||||
this.genericSectionErrors.splice(index);
|
this.genericSectionErrors.splice(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all errors from list
|
||||||
|
*/
|
||||||
public resetErrors() {
|
public resetErrors() {
|
||||||
if (isNotEmpty(this.genericSectionErrors)) {
|
if (isNotEmpty(this.genericSectionErrors)) {
|
||||||
this.sectionService.dispatchRemoveSectionErrors(this.submissionId, this.sectionId);
|
this.sectionService.dispatchRemoveSectionErrors(this.submissionId, this.sectionId);
|
||||||
|
@@ -35,9 +35,20 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
|||||||
import { SubmissionService } from '../submission.service';
|
import { SubmissionService } from '../submission.service';
|
||||||
import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model';
|
import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods used in submission process.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SectionsService {
|
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,
|
constructor(private notificationsService: NotificationsService,
|
||||||
private scrollToService: ScrollToService,
|
private scrollToService: ScrollToService,
|
||||||
private submissionService: SubmissionService,
|
private submissionService: SubmissionService,
|
||||||
@@ -45,17 +56,35 @@ export class SectionsService {
|
|||||||
private translate: TranslateService) {
|
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(
|
public checkSectionErrors(
|
||||||
submissionId: string,
|
submissionId: string,
|
||||||
sectionId: string,
|
sectionId: string,
|
||||||
formId: string,
|
formId: string,
|
||||||
currentErrors: SubmissionSectionError[],
|
currentErrors: SubmissionSectionError[],
|
||||||
prevErrors: SubmissionSectionError[] = []) {
|
prevErrors: SubmissionSectionError[] = []) {
|
||||||
|
// Remove previous error list if the current is empty
|
||||||
if (isEmpty(currentErrors)) {
|
if (isEmpty(currentErrors)) {
|
||||||
this.store.dispatch(new RemoveSectionErrorsAction(submissionId, sectionId));
|
this.store.dispatch(new RemoveSectionErrorsAction(submissionId, sectionId));
|
||||||
this.store.dispatch(new FormClearErrorsAction(formId));
|
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 = [];
|
const dispatchedErrors = [];
|
||||||
|
|
||||||
|
// Itereate over the current error list
|
||||||
currentErrors.forEach((error: SubmissionSectionError) => {
|
currentErrors.forEach((error: SubmissionSectionError) => {
|
||||||
const errorPaths: SectionErrorPath[] = parseSectionErrorPaths(error.path);
|
const errorPaths: SectionErrorPath[] = parseSectionErrorPaths(error.path);
|
||||||
|
|
||||||
@@ -63,7 +92,7 @@ export class SectionsService {
|
|||||||
if (path.fieldId) {
|
if (path.fieldId) {
|
||||||
const fieldId = path.fieldId.replace(/\./g, '_');
|
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);
|
const formAddErrorAction = new FormAddError(formId, fieldId, path.fieldIndex, error.message);
|
||||||
this.store.dispatch(formAddErrorAction);
|
this.store.dispatch(formAddErrorAction);
|
||||||
dispatchedErrors.push(fieldId);
|
dispatchedErrors.push(fieldId);
|
||||||
@@ -71,6 +100,7 @@ export class SectionsService {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Itereate over the previous error list
|
||||||
prevErrors.forEach((error: SubmissionSectionError) => {
|
prevErrors.forEach((error: SubmissionSectionError) => {
|
||||||
const errorPaths: SectionErrorPath[] = parseSectionErrorPaths(error.path);
|
const errorPaths: SectionErrorPath[] = parseSectionErrorPaths(error.path);
|
||||||
|
|
||||||
@@ -79,6 +109,7 @@ export class SectionsService {
|
|||||||
const fieldId = path.fieldId.replace(/\./g, '_');
|
const fieldId = path.fieldId.replace(/\./g, '_');
|
||||||
|
|
||||||
if (!dispatchedErrors.includes(fieldId)) {
|
if (!dispatchedErrors.includes(fieldId)) {
|
||||||
|
// Dispatch action to remove form error from the state;
|
||||||
const formRemoveErrorAction = new FormRemoveErrorAction(formId, fieldId, path.fieldIndex);
|
const formRemoveErrorAction = new FormRemoveErrorAction(formId, fieldId, path.fieldIndex);
|
||||||
this.store.dispatch(formRemoveErrorAction);
|
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) {
|
public dispatchRemoveSectionErrors(submissionId, sectionId) {
|
||||||
this.store.dispatch(new RemoveSectionErrorsAction(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> {
|
public getSectionData(submissionId: string, sectionId: string): Observable<WorkspaceitemSectionDataType> {
|
||||||
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
distinctUntilChanged());
|
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[]> {
|
public getSectionErrors(submissionId: string, sectionId: string): Observable<SubmissionSectionError[]> {
|
||||||
return this.store.select(submissionSectionErrorsFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionSectionErrorsFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
distinctUntilChanged());
|
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> {
|
public getSectionState(submissionId: string, sectionId: string): Observable<SubmissionSectionObject> {
|
||||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
filter((sectionObj: SubmissionSectionObject) => hasValue(sectionObj)),
|
filter((sectionObj: SubmissionSectionObject) => hasValue(sectionObj)),
|
||||||
@@ -109,6 +178,16 @@ export class SectionsService {
|
|||||||
distinctUntilChanged());
|
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> {
|
public isSectionValid(submissionId: string, sectionId: string): Observable<boolean> {
|
||||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
filter((sectionObj) => hasValue(sectionObj)),
|
filter((sectionObj) => hasValue(sectionObj)),
|
||||||
@@ -116,12 +195,32 @@ export class SectionsService {
|
|||||||
distinctUntilChanged());
|
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> {
|
public isSectionActive(submissionId: string, sectionId: string): Observable<boolean> {
|
||||||
return this.submissionService.getActiveSectionId(submissionId).pipe(
|
return this.submissionService.getActiveSectionId(submissionId).pipe(
|
||||||
map((activeSectionId: string) => sectionId === activeSectionId),
|
map((activeSectionId: string) => sectionId === activeSectionId),
|
||||||
distinctUntilChanged());
|
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> {
|
public isSectionEnabled(submissionId: string, sectionId: string): Observable<boolean> {
|
||||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
filter((sectionObj) => hasValue(sectionObj)),
|
filter((sectionObj) => hasValue(sectionObj)),
|
||||||
@@ -129,6 +228,18 @@ export class SectionsService {
|
|||||||
distinctUntilChanged());
|
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> {
|
public isSectionReadOnly(submissionId: string, sectionId: string, submissionScope: SubmissionScopeType): Observable<boolean> {
|
||||||
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionSectionFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
filter((sectionObj) => hasValue(sectionObj)),
|
filter((sectionObj) => hasValue(sectionObj)),
|
||||||
@@ -140,6 +251,16 @@ export class SectionsService {
|
|||||||
distinctUntilChanged());
|
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> {
|
public isSectionAvailable(submissionId: string, sectionId: string): Observable<boolean> {
|
||||||
return this.store.select(submissionObjectFromIdSelector(submissionId)).pipe(
|
return this.store.select(submissionObjectFromIdSelector(submissionId)).pipe(
|
||||||
filter((submissionState: SubmissionObjectEntry) => isNotUndefined(submissionState)),
|
filter((submissionState: SubmissionObjectEntry) => isNotUndefined(submissionState)),
|
||||||
@@ -149,8 +270,15 @@ export class SectionsService {
|
|||||||
distinctUntilChanged());
|
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));
|
this.store.dispatch(new EnableSectionAction(submissionId, sectionId));
|
||||||
const config: ScrollToConfigOptions = {
|
const config: ScrollToConfigOptions = {
|
||||||
target: sectionId,
|
target: sectionId,
|
||||||
@@ -160,11 +288,31 @@ export class SectionsService {
|
|||||||
this.scrollToService.scrollTo(config);
|
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) {
|
public removeSection(submissionId: string, sectionId: string) {
|
||||||
this.store.dispatch(new DisableSectionAction(submissionId, sectionId))
|
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)) {
|
if (isNotEmpty(data)) {
|
||||||
const isAvailable$ = this.isSectionAvailable(submissionId, sectionId);
|
const isAvailable$ = this.isSectionAvailable(submissionId, sectionId);
|
||||||
const isEnabled$ = this.isSectionEnabled(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) {
|
public setSectionError(submissionId: string, sectionId: string, error: SubmissionSectionError) {
|
||||||
this.store.dispatch(new InertSectionErrorsAction(submissionId, sectionId, error));
|
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) {
|
public setSectionStatus(submissionId: string, sectionId: string, status: boolean) {
|
||||||
this.store.dispatch(new SectionStatusChangeAction(submissionId, sectionId, status));
|
this.store.dispatch(new SectionStatusChangeAction(submissionId, sectionId, status));
|
||||||
}
|
}
|
||||||
|
@@ -8,18 +8,37 @@ import { isEmpty } from '../../../../shared/empty.util';
|
|||||||
import { Group } from '../../../../core/eperson/models/group.model';
|
import { Group } from '../../../../core/eperson/models/group.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a badge that describe an access condition
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-access-conditions',
|
selector: 'ds-submission-section-upload-access-conditions',
|
||||||
templateUrl: './accessConditions.component.html',
|
templateUrl: './submission-section-upload-access-conditions.component.html',
|
||||||
})
|
})
|
||||||
export class AccessConditionsComponent implements OnInit {
|
export class SubmissionSectionUploadAccessConditionsComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of resource policy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
@Input() accessConditions: ResourcePolicy[];
|
@Input() accessConditions: ResourcePolicy[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of access conditions
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
public accessConditionsList = [];
|
public accessConditionsList = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {GroupEpersonService} groupService
|
||||||
|
*/
|
||||||
constructor(private groupService: GroupEpersonService) {}
|
constructor(private groupService: GroupEpersonService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve access conditions list
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.accessConditions.forEach((accessCondition: ResourcePolicy) => {
|
this.accessConditions.forEach((accessCondition: ResourcePolicy) => {
|
||||||
if (isEmpty(accessCondition.name)) {
|
if (isEmpty(accessCondition.name)) {
|
@@ -15,7 +15,7 @@ import { createTestComponent } from '../../../../../shared/testing/utils';
|
|||||||
import { FormBuilderService } from '../../../../../shared/form/builder/form-builder.service';
|
import { FormBuilderService } from '../../../../../shared/form/builder/form-builder.service';
|
||||||
import { SubmissionServiceStub } from '../../../../../shared/testing/submission-service-stub';
|
import { SubmissionServiceStub } from '../../../../../shared/testing/submission-service-stub';
|
||||||
import { SubmissionService } from '../../../../submission.service';
|
import { SubmissionService } from '../../../../submission.service';
|
||||||
import { UploadSectionFileEditComponent } from './file-edit.component';
|
import { SubmissionSectionUploadFileEditComponent } from './section-upload-file-edit.component';
|
||||||
import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component';
|
import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component';
|
||||||
import {
|
import {
|
||||||
mockGroup,
|
mockGroup,
|
||||||
@@ -30,12 +30,13 @@ import { FormService } from '../../../../../shared/form/form.service';
|
|||||||
import { GLOBAL_CONFIG } from '../../../../../../config';
|
import { GLOBAL_CONFIG } from '../../../../../../config';
|
||||||
import { MOCK_SUBMISSION_CONFIG } from '../../../../../shared/testing/mock-submission-config';
|
import { MOCK_SUBMISSION_CONFIG } from '../../../../../shared/testing/mock-submission-config';
|
||||||
import { getMockFormService } from '../../../../../shared/mocks/mock-form-service';
|
import { getMockFormService } from '../../../../../shared/mocks/mock-form-service';
|
||||||
|
import { Group } from '../../../../../core/eperson/models/group.model';
|
||||||
|
|
||||||
describe('UploadSectionFileEditComponent test suite', () => {
|
describe('SubmissionSectionUploadFileEditComponent test suite', () => {
|
||||||
|
|
||||||
let comp: UploadSectionFileEditComponent;
|
let comp: SubmissionSectionUploadFileEditComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<UploadSectionFileEditComponent>;
|
let fixture: ComponentFixture<SubmissionSectionUploadFileEditComponent>;
|
||||||
let submissionServiceStub: SubmissionServiceStub;
|
let submissionServiceStub: SubmissionServiceStub;
|
||||||
let formbuilderService: any;
|
let formbuilderService: any;
|
||||||
|
|
||||||
@@ -44,7 +45,10 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
const sectionId = 'upload';
|
const sectionId = 'upload';
|
||||||
const collectionId = mockSubmissionCollectionId;
|
const collectionId = mockSubmissionCollectionId;
|
||||||
const availableAccessConditionOptions = mockUploadConfigResponse.accessConditionOptions;
|
const availableAccessConditionOptions = mockUploadConfigResponse.accessConditionOptions;
|
||||||
const availableGroupsMap = new Map([[mockGroup.id, { name: mockGroup.name, uuid: mockGroup.uuid }]]);
|
const availableGroupsMap: Map<string, Group[]> = new Map([
|
||||||
|
[mockUploadConfigResponse.accessConditionOptions[1].name, [mockGroup as any]],
|
||||||
|
[mockUploadConfigResponse.accessConditionOptions[2].name, [mockGroup as any]],
|
||||||
|
]);
|
||||||
const collectionPolicyType = POLICY_DEFAULT_WITH_LIST;
|
const collectionPolicyType = POLICY_DEFAULT_WITH_LIST;
|
||||||
const configMetadataForm: any = mockUploadConfigResponse.metadata;
|
const configMetadataForm: any = mockUploadConfigResponse.metadata;
|
||||||
const fileIndex = '0';
|
const fileIndex = '0';
|
||||||
@@ -62,7 +66,7 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
FormComponent,
|
FormComponent,
|
||||||
UploadSectionFileEditComponent,
|
SubmissionSectionUploadFileEditComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -71,7 +75,7 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
||||||
FormBuilderService,
|
FormBuilderService,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
UploadSectionFileEditComponent
|
SubmissionSectionUploadFileEditComponent
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents().then();
|
}).compileComponents().then();
|
||||||
@@ -84,7 +88,7 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
// synchronous beforeEach
|
// synchronous beforeEach
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const html = `
|
const html = `
|
||||||
<ds-submission-upload-section-file-edit [availableAccessConditionGroups]="availableAccessConditionGroups"
|
<ds-submission-section-upload-file-edit [availableAccessConditionGroups]="availableAccessConditionGroups"
|
||||||
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
||||||
[collectionId]="collectionId"
|
[collectionId]="collectionId"
|
||||||
[collectionPolicyType]="collectionPolicyType"
|
[collectionPolicyType]="collectionPolicyType"
|
||||||
@@ -93,7 +97,7 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
[fileId]="fileId"
|
[fileId]="fileId"
|
||||||
[fileIndex]="fileIndex"
|
[fileIndex]="fileIndex"
|
||||||
[formId]="formId"
|
[formId]="formId"
|
||||||
[sectionId]="sectionId"></ds-submission-upload-section-file-edit>`;
|
[sectionId]="sectionId"></ds-submission-section-upload-file-edit>`;
|
||||||
|
|
||||||
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
||||||
testComp = testFixture.componentInstance;
|
testComp = testFixture.componentInstance;
|
||||||
@@ -103,7 +107,7 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
testFixture.destroy();
|
testFixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create UploadSectionFileEditComponent', inject([UploadSectionFileEditComponent], (app: UploadSectionFileEditComponent) => {
|
it('should create SubmissionSectionUploadFileEditComponent', inject([SubmissionSectionUploadFileEditComponent], (app: SubmissionSectionUploadFileEditComponent) => {
|
||||||
|
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
|
|
||||||
@@ -112,7 +116,7 @@ describe('UploadSectionFileEditComponent test suite', () => {
|
|||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(UploadSectionFileEditComponent);
|
fixture = TestBed.createComponent(SubmissionSectionUploadFileEditComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
submissionServiceStub = TestBed.get(SubmissionService);
|
submissionServiceStub = TestBed.get(SubmissionService);
|
@@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectorRef, Component, Input, OnChanges } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
|
||||||
import { WorkspaceitemSectionUploadFileObject } from '../../../../../core/submission/models/workspaceitem-section-upload-file.model';
|
|
||||||
import {
|
import {
|
||||||
DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER,
|
DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER,
|
||||||
DynamicDateControlModel,
|
DynamicDateControlModel,
|
||||||
@@ -12,6 +12,8 @@ import {
|
|||||||
DynamicFormGroupModel,
|
DynamicFormGroupModel,
|
||||||
DynamicSelectModel
|
DynamicSelectModel
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
|
|
||||||
|
import { WorkspaceitemSectionUploadFileObject } from '../../../../../core/submission/models/workspaceitem-section-upload-file.model';
|
||||||
import { FormBuilderService } from '../../../../../shared/form/builder/form-builder.service';
|
import { FormBuilderService } from '../../../../../shared/form/builder/form-builder.service';
|
||||||
import {
|
import {
|
||||||
BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_CONFIG,
|
BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_CONFIG,
|
||||||
@@ -26,39 +28,122 @@ import {
|
|||||||
BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT,
|
BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT,
|
||||||
BITSTREAM_METADATA_FORM_GROUP_CONFIG,
|
BITSTREAM_METADATA_FORM_GROUP_CONFIG,
|
||||||
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
BITSTREAM_METADATA_FORM_GROUP_LAYOUT
|
||||||
} from './files-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, isNotUndefined } from '../../../../../shared/empty.util';
|
import { isNotEmpty, isNotUndefined } 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 { FormComponent } from '../../../../../shared/form/form.component';
|
||||||
|
import { Group } from '../../../../../core/eperson/models/group.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents the edit form for bitstream
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-upload-section-file-edit',
|
selector: 'ds-submission-section-upload-file-edit',
|
||||||
templateUrl: './file-edit.component.html',
|
templateUrl: './section-upload-file-edit.component.html',
|
||||||
})
|
})
|
||||||
export class UploadSectionFileEditComponent implements OnChanges {
|
export class SubmissionSectionUploadFileEditComponent implements OnChanges {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available access condition
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
@Input() availableAccessConditionOptions: any[];
|
@Input() availableAccessConditionOptions: any[];
|
||||||
@Input() availableAccessConditionGroups: Map<string, any>;
|
|
||||||
@Input() collectionId;
|
|
||||||
@Input() collectionPolicyType;
|
|
||||||
@Input() configMetadataForm: SubmissionFormsModel;
|
|
||||||
@Input() fileData: WorkspaceitemSectionUploadFileObject;
|
|
||||||
@Input() fileId;
|
|
||||||
@Input() fileIndex;
|
|
||||||
@Input() formId;
|
|
||||||
@Input() sectionId;
|
|
||||||
@Input() submissionId;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available groups for an access condition
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
@Input() availableAccessConditionGroups: Map<string, Group[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if collection access conditions policy type :
|
||||||
|
* POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file
|
||||||
|
* POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
@Input() collectionPolicyType: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration for the bitstream's metadata form
|
||||||
|
* @type {SubmissionFormsModel}
|
||||||
|
*/
|
||||||
|
@Input() configMetadataForm: SubmissionFormsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream's metadata data
|
||||||
|
* @type {WorkspaceitemSectionUploadFileObject}
|
||||||
|
*/
|
||||||
|
@Input() fileData: WorkspaceitemSectionUploadFileObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream array key
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileIndex: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() formId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() sectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form model
|
||||||
|
* @type {DynamicFormControlModel[]}
|
||||||
|
*/
|
||||||
public formModel: DynamicFormControlModel[];
|
public formModel: DynamicFormControlModel[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FormComponent reference
|
||||||
|
*/
|
||||||
|
@ViewChild('formRef') public formRef: FormComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} cdr
|
||||||
|
* @param {FormBuilderService} formBuilderService
|
||||||
|
* @param {FormService} formService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
*/
|
||||||
constructor(private cdr: ChangeDetectorRef,
|
constructor(private cdr: ChangeDetectorRef,
|
||||||
private formBuilderService: FormBuilderService,
|
private formBuilderService: FormBuilderService,
|
||||||
|
private formService: FormService,
|
||||||
private submissionService: SubmissionService) {
|
private submissionService: SubmissionService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch form model init
|
||||||
|
*/
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.fileData && this.formId) {
|
if (this.fileData && this.formId) {
|
||||||
this.formModel = this.buildFileEditForm();
|
this.formModel = this.buildFileEditForm();
|
||||||
@@ -66,8 +151,10 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize form model
|
||||||
|
*/
|
||||||
protected buildFileEditForm() {
|
protected buildFileEditForm() {
|
||||||
// TODO check in the rest server configuration whether dc.description may be repeatable
|
|
||||||
const configDescr: FormFieldModel = Object.assign({}, this.configMetadataForm.rows[0].fields[0]);
|
const configDescr: FormFieldModel = Object.assign({}, this.configMetadataForm.rows[0].fields[0]);
|
||||||
configDescr.repeatable = false;
|
configDescr.repeatable = false;
|
||||||
const configForm = Object.assign({}, this.configMetadataForm, {
|
const configForm = Object.assign({}, this.configMetadataForm, {
|
||||||
@@ -100,7 +187,7 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
accessConditionTypeModelConfig.options = accessConditionTypeOptions;
|
accessConditionTypeModelConfig.options = accessConditionTypeOptions;
|
||||||
|
|
||||||
// Dynamic assign of relation in config. For startdate, endDate, groups.
|
// Dynamically assign of relation in config. For startdate, endDate, groups.
|
||||||
const hasStart = [];
|
const hasStart = [];
|
||||||
const hasEnd = [];
|
const hasEnd = [];
|
||||||
const hasGroups = [];
|
const hasGroups = [];
|
||||||
@@ -146,6 +233,12 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
return formModel;
|
return formModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize form model values
|
||||||
|
*
|
||||||
|
* @param formModel
|
||||||
|
* The form model
|
||||||
|
*/
|
||||||
public initModelData(formModel: DynamicFormControlModel[]) {
|
public initModelData(formModel: DynamicFormControlModel[]) {
|
||||||
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
this.fileData.accessConditions.forEach((accessCondition, index) => {
|
||||||
Array.of('name', 'groupUUID', 'startDate', 'endDate')
|
Array.of('name', 'groupUUID', 'startDate', 'endDate')
|
||||||
@@ -153,8 +246,8 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
.forEach((key) => {
|
.forEach((key) => {
|
||||||
const metadataModel: any = this.formBuilderService.findById(key, formModel, index);
|
const metadataModel: any = this.formBuilderService.findById(key, formModel, index);
|
||||||
if (metadataModel) {
|
if (metadataModel) {
|
||||||
if (key === 'groupUUID') {
|
if (key === 'groupUUID' && this.availableAccessConditionGroups.get(accessCondition.name)) {
|
||||||
this.availableAccessConditionGroups.forEach((group) => {
|
this.availableAccessConditionGroups.get(accessCondition.name).forEach((group) => {
|
||||||
metadataModel.options.push({
|
metadataModel.options.push({
|
||||||
label: group.name,
|
label: group.name,
|
||||||
value: group.uuid
|
value: group.uuid
|
||||||
@@ -176,27 +269,47 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch form model update when changing an access condition
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event emitted
|
||||||
|
*/
|
||||||
public onChange(event: DynamicFormControlEvent) {
|
public onChange(event: DynamicFormControlEvent) {
|
||||||
if (event.model.id === 'name') {
|
if (event.model.id === 'name') {
|
||||||
this.setOptions(event.model, event.control);
|
this.setOptions(event.model, event.control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setOptions(model, 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;
|
let accessCondition: AccessConditionOption = null;
|
||||||
this.availableAccessConditionOptions.filter((element) => element.name === control.value)
|
this.availableAccessConditionOptions.filter((element) => element.name === control.value)
|
||||||
.forEach((element) => accessCondition = element);
|
.forEach((element) => accessCondition = element);
|
||||||
if (isNotEmpty(accessCondition)) {
|
if (isNotEmpty(accessCondition)) {
|
||||||
const showGroups: boolean = accessCondition.hasStartDate === true || accessCondition.hasEndDate === true;
|
const showGroups: boolean = accessCondition.hasStartDate === true || accessCondition.hasEndDate === true;
|
||||||
|
|
||||||
const groupControl = control.parent.get('groupUUID');
|
const groupControl: FormControl = control.parent.get('groupUUID') as FormControl;
|
||||||
const startDateControl = control.parent.get('startDate');
|
const startDateControl: FormControl = control.parent.get('startDate') as FormControl;
|
||||||
const endDateControl = control.parent.get('endDate');
|
const endDateControl: FormControl = control.parent.get('endDate') as FormControl;
|
||||||
|
|
||||||
|
// Clear previous state
|
||||||
|
groupControl.markAsUntouched();
|
||||||
|
startDateControl.markAsUntouched();
|
||||||
|
endDateControl.markAsUntouched();
|
||||||
|
|
||||||
// Clear previous values
|
// Clear previous values
|
||||||
if (showGroups) {
|
if (showGroups) {
|
||||||
groupControl.setValue(null);
|
groupControl.setValue(null);
|
||||||
} else {
|
} else {
|
||||||
|
groupControl.clearValidators();
|
||||||
groupControl.setValue(accessCondition.groupUUID);
|
groupControl.setValue(accessCondition.groupUUID);
|
||||||
}
|
}
|
||||||
startDateControl.setValue(null);
|
startDateControl.setValue(null);
|
||||||
@@ -204,15 +317,15 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
endDateControl.setValue(null);
|
endDateControl.setValue(null);
|
||||||
|
|
||||||
if (showGroups) {
|
if (showGroups) {
|
||||||
if (isNotUndefined(accessCondition.groupUUID)) {
|
if (isNotUndefined(accessCondition.groupUUID) || isNotUndefined(accessCondition.selectGroupUUID)) {
|
||||||
|
|
||||||
const groupOptions = [];
|
const groupOptions = [];
|
||||||
if (isNotUndefined(this.availableAccessConditionGroups.get(accessCondition.groupUUID))) {
|
if (isNotUndefined(this.availableAccessConditionGroups.get(accessCondition.name))) {
|
||||||
const groupModel = this.formBuilderService.findById(
|
const groupModel = this.formBuilderService.findById(
|
||||||
'groupUUID',
|
'groupUUID',
|
||||||
(model.parent as DynamicFormArrayGroupModel).group) as DynamicSelectModel<any>;
|
(model.parent as DynamicFormArrayGroupModel).group) as DynamicSelectModel<any>;
|
||||||
|
|
||||||
this.availableAccessConditionGroups.forEach((group) => {
|
this.availableAccessConditionGroups.get(accessCondition.name).forEach((group) => {
|
||||||
groupOptions.push({
|
groupOptions.push({
|
||||||
label: group.name,
|
label: group.name,
|
||||||
value: group.uuid
|
value: group.uuid
|
||||||
@@ -223,8 +336,8 @@ export class UploadSectionFileEditComponent implements OnChanges {
|
|||||||
const confGroup = { relation: groupModel.relation };
|
const confGroup = { relation: groupModel.relation };
|
||||||
const groupsConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_CONFIG, confGroup);
|
const groupsConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_CONFIG, confGroup);
|
||||||
groupsConfig.options = groupOptions;
|
groupsConfig.options = groupOptions;
|
||||||
model.parent.group.pop();
|
(model.parent as DynamicFormGroupModel).group.pop();
|
||||||
model.parent.group.push(new DynamicSelectModel(groupsConfig, BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_LAYOUT));
|
(model.parent as DynamicFormGroupModel).group.push(new DynamicSelectModel(groupsConfig, BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_LAYOUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -56,7 +56,14 @@ export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG: DynamicDatePicke
|
|||||||
connective: 'OR',
|
connective: 'OR',
|
||||||
when: []
|
when: []
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
required: true,
|
||||||
|
validators: {
|
||||||
|
required: null
|
||||||
|
},
|
||||||
|
errorMessages: {
|
||||||
|
required: 'submission.sections.upload.form.date-required'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT: DynamicFormControlLayout = {
|
export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT: DynamicFormControlLayout = {
|
||||||
element: {
|
element: {
|
||||||
@@ -80,7 +87,14 @@ export const BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_CONFIG: DynamicDatePickerM
|
|||||||
connective: 'OR',
|
connective: 'OR',
|
||||||
when: []
|
when: []
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
required: true,
|
||||||
|
validators: {
|
||||||
|
required: null
|
||||||
|
},
|
||||||
|
errorMessages: {
|
||||||
|
required: 'submission.sections.upload.form.date-required'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export const BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT: DynamicFormControlLayout = {
|
export const BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT: DynamicFormControlLayout = {
|
||||||
element: {
|
element: {
|
||||||
@@ -102,7 +116,14 @@ export const BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_CONFIG: DynamicSelectModelCo
|
|||||||
connective: 'OR',
|
connective: 'OR',
|
||||||
when: []
|
when: []
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
required: true,
|
||||||
|
validators: {
|
||||||
|
required: null
|
||||||
|
},
|
||||||
|
errorMessages: {
|
||||||
|
required: 'submission.sections.upload.form.group-required'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export const BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_LAYOUT: DynamicFormControlLayout = {
|
export const BITSTREAM_FORM_ACCESS_CONDITION_GROUPS_LAYOUT: DynamicFormControlLayout = {
|
||||||
element: {
|
element: {
|
@@ -34,9 +34,9 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<ds-submission-upload-section-file-view *ngIf="readMode"
|
<ds-submission-section-upload-file-view *ngIf="readMode"
|
||||||
[fileData]="fileData"></ds-submission-upload-section-file-view>
|
[fileData]="fileData"></ds-submission-section-upload-file-view>
|
||||||
<ds-submission-upload-section-file-edit *ngIf="!readMode"
|
<ds-submission-section-upload-file-edit *ngIf="!readMode"
|
||||||
[availableAccessConditionGroups]="availableAccessConditionGroups"
|
[availableAccessConditionGroups]="availableAccessConditionGroups"
|
||||||
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
[availableAccessConditionOptions]="availableAccessConditionOptions"
|
||||||
[collectionId]="collectionId"
|
[collectionId]="collectionId"
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
[fileId]="fileId"
|
[fileId]="fileId"
|
||||||
[fileIndex]="fileIndex"
|
[fileIndex]="fileIndex"
|
||||||
[formId]="formId"
|
[formId]="formId"
|
||||||
[sectionId]="sectionId"></ds-submission-upload-section-file-edit>
|
[sectionId]="sectionId"></ds-submission-section-upload-file-edit>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
@@ -15,7 +15,7 @@ import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder';
|
import { JsonPatchOperationsBuilder } from '../../../../core/json-patch/builder/json-patch-operations-builder';
|
||||||
import { SubmissionJsonPatchOperationsServiceStub } from '../../../../shared/testing/submission-json-patch-operations-service-stub';
|
import { SubmissionJsonPatchOperationsServiceStub } from '../../../../shared/testing/submission-json-patch-operations-service-stub';
|
||||||
import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.service';
|
import { SubmissionJsonPatchOperationsService } from '../../../../core/submission/submission-json-patch-operations.service';
|
||||||
import { UploadSectionFileComponent } from './file.component';
|
import { SubmissionSectionUploadFileComponent } from './section-upload-file.component';
|
||||||
import { SubmissionServiceStub } from '../../../../shared/testing/submission-service-stub';
|
import { SubmissionServiceStub } from '../../../../shared/testing/submission-service-stub';
|
||||||
import {
|
import {
|
||||||
mockFileFormData,
|
mockFileFormData,
|
||||||
@@ -35,6 +35,8 @@ import { POLICY_DEFAULT_WITH_LIST } from '../section-upload.component';
|
|||||||
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 { getMockSectionUploadService } from '../../../../shared/mocks/mock-section-upload.service';
|
import { getMockSectionUploadService } from '../../../../shared/mocks/mock-section-upload.service';
|
||||||
import { FormFieldMetadataValueObject } from '../../../../shared/form/builder/models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../../../shared/form/builder/models/form-field-metadata-value.model';
|
||||||
|
import { Group } from '../../../../core/eperson/models/group.model';
|
||||||
|
import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
|
||||||
|
|
||||||
function getMockFileService(): FileService {
|
function getMockFileService(): FileService {
|
||||||
return jasmine.createSpyObj('FileService', {
|
return jasmine.createSpyObj('FileService', {
|
||||||
@@ -43,11 +45,11 @@ function getMockFileService(): FileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('UploadSectionFileComponent test suite', () => {
|
describe('SubmissionSectionUploadFileComponent test suite', () => {
|
||||||
|
|
||||||
let comp: UploadSectionFileComponent;
|
let comp: SubmissionSectionUploadFileComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<UploadSectionFileComponent>;
|
let fixture: ComponentFixture<SubmissionSectionUploadFileComponent>;
|
||||||
let submissionServiceStub: SubmissionServiceStub;
|
let submissionServiceStub: SubmissionServiceStub;
|
||||||
let uploadService: any;
|
let uploadService: any;
|
||||||
let fileService: any;
|
let fileService: any;
|
||||||
@@ -61,7 +63,10 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
const sectionId = 'upload';
|
const sectionId = 'upload';
|
||||||
const collectionId = mockSubmissionCollectionId;
|
const collectionId = mockSubmissionCollectionId;
|
||||||
const availableAccessConditionOptions = mockUploadConfigResponse.accessConditionOptions;
|
const availableAccessConditionOptions = mockUploadConfigResponse.accessConditionOptions;
|
||||||
const availableGroupsMap = new Map([[mockGroup.id, { name: mockGroup.name, uuid: mockGroup.uuid }]]);
|
const availableGroupsMap: Map<string, Group[]> = new Map([
|
||||||
|
[mockUploadConfigResponse.accessConditionOptions[1].name, [mockGroup as any]],
|
||||||
|
[mockUploadConfigResponse.accessConditionOptions[2].name, [mockGroup as any]],
|
||||||
|
]);
|
||||||
const collectionPolicyType = POLICY_DEFAULT_WITH_LIST;
|
const collectionPolicyType = POLICY_DEFAULT_WITH_LIST;
|
||||||
const fileIndex = '0';
|
const fileIndex = '0';
|
||||||
const fileName = '123456-test-upload.jpg';
|
const fileName = '123456-test-upload.jpg';
|
||||||
@@ -85,7 +90,7 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
FileSizePipe,
|
FileSizePipe,
|
||||||
UploadSectionFileComponent,
|
SubmissionSectionUploadFileComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -98,7 +103,8 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
{ provide: SectionUploadService, useValue: getMockSectionUploadService() },
|
{ provide: SectionUploadService, useValue: getMockSectionUploadService() },
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
NgbModal,
|
NgbModal,
|
||||||
UploadSectionFileComponent
|
SubmissionSectionUploadFileComponent,
|
||||||
|
SubmissionSectionUploadFileEditComponent
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents().then();
|
}).compileComponents().then();
|
||||||
@@ -130,7 +136,7 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
testFixture.destroy();
|
testFixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create UploadSectionFileComponent', inject([UploadSectionFileComponent], (app: UploadSectionFileComponent) => {
|
it('should create SubmissionSectionUploadFileComponent', inject([SubmissionSectionUploadFileComponent], (app: SubmissionSectionUploadFileComponent) => {
|
||||||
|
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
|
|
||||||
@@ -139,7 +145,7 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(UploadSectionFileComponent);
|
fixture = TestBed.createComponent(SubmissionSectionUploadFileComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
submissionServiceStub = TestBed.get(SubmissionService);
|
submissionServiceStub = TestBed.get(SubmissionService);
|
||||||
@@ -228,10 +234,14 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
expect(fileService.downloadFile).toHaveBeenCalled()
|
expect(fileService.downloadFile).toHaveBeenCalled()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should download Bitstream File properly', fakeAsync(() => {
|
it('should save Bitstream File data properly when form is valid', fakeAsync(() => {
|
||||||
|
compAsAny.fileEditComp = TestBed.get(SubmissionSectionUploadFileEditComponent);
|
||||||
|
compAsAny.fileEditComp.formRef = {formGroup: null};
|
||||||
compAsAny.pathCombiner = pathCombiner;
|
compAsAny.pathCombiner = pathCombiner;
|
||||||
const event = new Event('click', null);
|
const event = new Event('click', null);
|
||||||
spyOn(comp, 'switchMode');
|
spyOn(comp, 'switchMode');
|
||||||
|
formService.validateAllFormFields.and.callFake(() => null);
|
||||||
|
formService.isValid.and.returnValue(observableOf(true));
|
||||||
formService.getFormData.and.returnValue(observableOf(mockFileFormData));
|
formService.getFormData.and.returnValue(observableOf(mockFileFormData));
|
||||||
|
|
||||||
const response = [
|
const response = [
|
||||||
@@ -279,6 +289,20 @@ describe('UploadSectionFileComponent test suite', () => {
|
|||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should not save Bitstream File data properly when form is not valid', fakeAsync(() => {
|
||||||
|
compAsAny.fileEditComp = TestBed.get(SubmissionSectionUploadFileEditComponent);
|
||||||
|
compAsAny.fileEditComp.formRef = {formGroup: null};
|
||||||
|
compAsAny.pathCombiner = pathCombiner;
|
||||||
|
const event = new Event('click', null);
|
||||||
|
spyOn(comp, 'switchMode');
|
||||||
|
formService.validateAllFormFields.and.callFake(() => null);
|
||||||
|
formService.isValid.and.returnValue(observableOf(false));
|
||||||
|
|
||||||
|
expect(comp.switchMode).not.toHaveBeenCalled();
|
||||||
|
expect(uploadService.updateFileData).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
it('should retrieve Value From Field properly', () => {
|
it('should retrieve Value From Field properly', () => {
|
||||||
let field;
|
let field;
|
||||||
expect(compAsAny.retrieveValueFromField(field)).toBeUndefined();
|
expect(compAsAny.retrieveValueFromField(field)).toBeUndefined();
|
@@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectorRef, Component, Input, OnChanges, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||||
import { filter, first, flatMap } from 'rxjs/operators';
|
import { filter, first, flatMap, take } 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';
|
||||||
|
|
||||||
@@ -20,34 +20,142 @@ 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 { SubmissionObject } from '../../../../core/submission/models/submission-object.model';
|
||||||
import { WorkspaceitemSectionUploadObject } from '../../../../core/submission/models/workspaceitem-section-upload.model';
|
import { WorkspaceitemSectionUploadObject } from '../../../../core/submission/models/workspaceitem-section-upload.model';
|
||||||
|
import { SubmissionSectionUploadFileEditComponent } from './edit/section-upload-file-edit.component';
|
||||||
|
import { Group } from '../../../../core/eperson/models/group.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a single bitstream contained in the submission
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-upload-section-file',
|
selector: 'ds-submission-upload-section-file',
|
||||||
styleUrls: ['./file.component.scss'],
|
styleUrls: ['./section-upload-file.component.scss'],
|
||||||
templateUrl: './file.component.html',
|
templateUrl: './section-upload-file.component.html',
|
||||||
})
|
})
|
||||||
export class UploadSectionFileComponent implements OnChanges, OnInit {
|
export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available access condition
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
@Input() availableAccessConditionOptions: any[];
|
@Input() availableAccessConditionOptions: any[];
|
||||||
@Input() availableAccessConditionGroups: Map<string, any>;
|
|
||||||
@Input() collectionId;
|
|
||||||
@Input() collectionPolicyType;
|
|
||||||
@Input() configMetadataForm: SubmissionFormsModel;
|
|
||||||
@Input() fileId;
|
|
||||||
@Input() fileIndex;
|
|
||||||
@Input() fileName;
|
|
||||||
@Input() sectionId;
|
|
||||||
@Input() submissionId;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available groups for an access condition
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
@Input() availableAccessConditionGroups: Map<string, Group[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() collectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if collection access conditions policy type :
|
||||||
|
* POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file
|
||||||
|
* POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
@Input() collectionPolicyType: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration for the bitstream's metadata form
|
||||||
|
* @type {SubmissionFormsModel}
|
||||||
|
*/
|
||||||
|
@Input() configMetadataForm: SubmissionFormsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream array key
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileIndex: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() fileName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The section id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() sectionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submission id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() submissionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream's metadata data
|
||||||
|
* @type {WorkspaceitemSectionUploadFileObject}
|
||||||
|
*/
|
||||||
public fileData: WorkspaceitemSectionUploadFileObject;
|
public fileData: WorkspaceitemSectionUploadFileObject;
|
||||||
public formId;
|
|
||||||
public readMode;
|
/**
|
||||||
|
* The form id
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
public formId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if to show bitstream edit form
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
public readMode: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The form model
|
||||||
|
* @type {DynamicFormControlModel[]}
|
||||||
|
*/
|
||||||
public formModel: DynamicFormControlModel[];
|
public formModel: DynamicFormControlModel[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a submission delete operation is pending
|
||||||
|
* @type {BehaviorSubject<boolean>}
|
||||||
|
*/
|
||||||
public processingDelete$ = new BehaviorSubject<boolean>(false);
|
public processingDelete$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [JsonPatchOperationPathCombiner] object
|
||||||
|
* @type {JsonPatchOperationPathCombiner}
|
||||||
|
*/
|
||||||
protected pathCombiner: JsonPatchOperationPathCombiner;
|
protected pathCombiner: JsonPatchOperationPathCombiner;
|
||||||
protected subscriptions = [];
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
protected subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [[SubmissionSectionUploadFileEditComponent]] reference
|
||||||
|
* @type {SubmissionSectionUploadFileEditComponent}
|
||||||
|
*/
|
||||||
|
@ViewChild(SubmissionSectionUploadFileEditComponent) fileEditComp: SubmissionSectionUploadFileEditComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} cdr
|
||||||
|
* @param {FileService} fileService
|
||||||
|
* @param {FormService} formService
|
||||||
|
* @param {HALEndpointService} halService
|
||||||
|
* @param {NgbModal} modalService
|
||||||
|
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||||
|
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {SectionUploadService} uploadService
|
||||||
|
*/
|
||||||
constructor(private cdr: ChangeDetectorRef,
|
constructor(private cdr: ChangeDetectorRef,
|
||||||
private fileService: FileService,
|
private fileService: FileService,
|
||||||
private formService: FormService,
|
private formService: FormService,
|
||||||
@@ -60,6 +168,9 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
this.readMode = true;
|
this.readMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve bitstream's metadata
|
||||||
|
*/
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.availableAccessConditionOptions && this.availableAccessConditionGroups) {
|
if (this.availableAccessConditionOptions && this.availableAccessConditionGroups) {
|
||||||
// Retrieve file state
|
// Retrieve file state
|
||||||
@@ -75,11 +186,17 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*/
|
||||||
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, 'files', this.fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete bitstream from submission
|
||||||
|
*/
|
||||||
protected deleteFile() {
|
protected deleteFile() {
|
||||||
this.operationsBuilder.remove(this.pathCombiner.getPath());
|
this.operationsBuilder.remove(this.pathCombiner.getPath());
|
||||||
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
|
this.subscriptions.push(this.operationsService.jsonPatchByResourceID(
|
||||||
@@ -93,6 +210,9 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show confirmation dialog for delete
|
||||||
|
*/
|
||||||
public confirmDelete(content) {
|
public confirmDelete(content) {
|
||||||
this.modalService.open(content).result.then(
|
this.modalService.open(content).result.then(
|
||||||
(result) => {
|
(result) => {
|
||||||
@@ -104,6 +224,9 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform bitstream download
|
||||||
|
*/
|
||||||
public downloadBitstreamFile() {
|
public downloadBitstreamFile() {
|
||||||
this.halService.getEndpoint('bitstreams').pipe(
|
this.halService.getEndpoint('bitstreams').pipe(
|
||||||
first())
|
first())
|
||||||
@@ -113,12 +236,24 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save bitstream metadata
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the click event emitted
|
||||||
|
*/
|
||||||
public saveBitstreamData(event) {
|
public saveBitstreamData(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
this.subscriptions.push(this.formService.getFormData(this.formId).pipe(
|
// validate form
|
||||||
first(),
|
this.formService.validateAllFormFields(this.fileEditComp.formRef.formGroup);
|
||||||
|
this.subscriptions.push(this.formService.isValid(this.formId).pipe(
|
||||||
|
take(1),
|
||||||
|
filter((isValid) => isValid),
|
||||||
|
flatMap(() => this.formService.getFormData(this.formId)),
|
||||||
|
take(1),
|
||||||
flatMap((formData: any) => {
|
flatMap((formData: any) => {
|
||||||
|
// 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) => {
|
||||||
@@ -166,6 +301,7 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true);
|
this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dispatch a PATCH request to save metadata
|
||||||
return this.operationsService.jsonPatchByResourceID(
|
return this.operationsService.jsonPatchByResourceID(
|
||||||
this.submissionService.getSubmissionObjectLinkName(),
|
this.submissionService.getSubmissionObjectLinkName(),
|
||||||
this.submissionId,
|
this.submissionId,
|
||||||
@@ -186,11 +322,20 @@ export class UploadSectionFileComponent implements OnChanges, OnInit {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private retrieveValueFromField(field) {
|
/**
|
||||||
|
* Retrieve field value
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* the specified field object
|
||||||
|
*/
|
||||||
|
private retrieveValueFromField(field: any) {
|
||||||
const temp = Array.isArray(field) ? field[0] : field;
|
const temp = Array.isArray(field) ? field[0] : field;
|
||||||
return (temp) ? temp.value : undefined;
|
return (temp) ? temp.value : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch from edit form to metadata view
|
||||||
|
*/
|
||||||
public switchMode() {
|
public switchMode() {
|
||||||
this.readMode = !this.readMode;
|
this.readMode = !this.readMode;
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
@@ -25,5 +25,5 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<span class="clearfix"></span>
|
<span class="clearfix"></span>
|
||||||
<ds-access-conditions [accessConditions]="fileData.accessConditions"></ds-access-conditions>
|
<ds-submission-section-upload-access-conditions [accessConditions]="fileData.accessConditions"></ds-submission-section-upload-access-conditions>
|
||||||
</div>
|
</div>
|
@@ -6,15 +6,15 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { createTestComponent } from '../../../../../shared/testing/utils';
|
import { createTestComponent } from '../../../../../shared/testing/utils';
|
||||||
import { mockUploadFiles } from '../../../../../shared/mocks/mock-submission';
|
import { mockUploadFiles } from '../../../../../shared/mocks/mock-submission';
|
||||||
import { FormComponent } from '../../../../../shared/form/form.component';
|
import { FormComponent } from '../../../../../shared/form/form.component';
|
||||||
import { UploadSectionFileViewComponent } from './file-view.component';
|
import { SubmissionSectionUploadFileViewComponent } from './section-upload-file-view.component';
|
||||||
import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe';
|
import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe';
|
||||||
import { Metadata } from '../../../../../core/shared/metadata.utils';
|
import { Metadata } from '../../../../../core/shared/metadata.utils';
|
||||||
|
|
||||||
describe('UploadSectionFileViewComponent test suite', () => {
|
describe('SubmissionSectionUploadFileViewComponent test suite', () => {
|
||||||
|
|
||||||
let comp: UploadSectionFileViewComponent;
|
let comp: SubmissionSectionUploadFileViewComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<UploadSectionFileViewComponent>;
|
let fixture: ComponentFixture<SubmissionSectionUploadFileViewComponent>;
|
||||||
|
|
||||||
const fileData: any = mockUploadFiles[0];
|
const fileData: any = mockUploadFiles[0];
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ describe('UploadSectionFileViewComponent test suite', () => {
|
|||||||
declarations: [
|
declarations: [
|
||||||
TruncatePipe,
|
TruncatePipe,
|
||||||
FormComponent,
|
FormComponent,
|
||||||
UploadSectionFileViewComponent,
|
SubmissionSectionUploadFileViewComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
UploadSectionFileViewComponent
|
SubmissionSectionUploadFileViewComponent
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents().then();
|
}).compileComponents().then();
|
||||||
@@ -43,7 +43,7 @@ describe('UploadSectionFileViewComponent test suite', () => {
|
|||||||
// synchronous beforeEach
|
// synchronous beforeEach
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const html = `
|
const html = `
|
||||||
<ds-submission-upload-section-file-view [fileData]="fileData"></ds-submission-upload-section-file-view>`;
|
<ds-submission-section-upload-file-view [fileData]="fileData"></ds-submission-section-upload-file-view>`;
|
||||||
|
|
||||||
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
|
||||||
testComp = testFixture.componentInstance;
|
testComp = testFixture.componentInstance;
|
||||||
@@ -53,7 +53,7 @@ describe('UploadSectionFileViewComponent test suite', () => {
|
|||||||
testFixture.destroy();
|
testFixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create UploadSectionFileViewComponent', inject([UploadSectionFileViewComponent], (app: UploadSectionFileViewComponent) => {
|
it('should create SubmissionSectionUploadFileViewComponent', inject([SubmissionSectionUploadFileViewComponent], (app: SubmissionSectionUploadFileViewComponent) => {
|
||||||
|
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ describe('UploadSectionFileViewComponent test suite', () => {
|
|||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(UploadSectionFileViewComponent);
|
fixture = TestBed.createComponent(SubmissionSectionUploadFileViewComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
});
|
});
|
@@ -5,17 +5,42 @@ import { isNotEmpty } from '../../../../../shared/empty.util';
|
|||||||
import { Metadata } from '../../../../../core/shared/metadata.utils';
|
import { Metadata } from '../../../../../core/shared/metadata.utils';
|
||||||
import { MetadataMap, MetadataValue } from '../../../../../core/shared/metadata.models';
|
import { MetadataMap, MetadataValue } from '../../../../../core/shared/metadata.models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component allow to show bitstream's metadata
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-upload-section-file-view',
|
selector: 'ds-submission-section-upload-file-view',
|
||||||
templateUrl: './file-view.component.html',
|
templateUrl: './section-upload-file-view.component.html',
|
||||||
})
|
})
|
||||||
export class UploadSectionFileViewComponent implements OnInit {
|
export class SubmissionSectionUploadFileViewComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream's metadata data
|
||||||
|
* @type {WorkspaceitemSectionUploadFileObject}
|
||||||
|
*/
|
||||||
@Input() fileData: WorkspaceitemSectionUploadFileObject;
|
@Input() fileData: WorkspaceitemSectionUploadFileObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [[MetadataMap]] object
|
||||||
|
* @type {MetadataMap}
|
||||||
|
*/
|
||||||
public metadata: MetadataMap = Object.create({});
|
public metadata: MetadataMap = Object.create({});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream's title key
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public fileTitleKey = 'Title';
|
public fileTitleKey = 'Title';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitstream's description key
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public fileDescrKey = 'Description';
|
public fileDescrKey = 'Description';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (isNotEmpty(this.fileData.metadata)) {
|
if (isNotEmpty(this.fileData.metadata)) {
|
||||||
this.metadata[this.fileTitleKey] = Metadata.all(this.fileData.metadata, 'dc.title');
|
this.metadata[this.fileTitleKey] = Metadata.all(this.fileData.metadata, 'dc.title');
|
||||||
@@ -23,7 +48,15 @@ export class UploadSectionFileViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllMetadataValue(metadataKey): MetadataValue[] {
|
/**
|
||||||
|
* Gets all matching metadata in the map(s)
|
||||||
|
*
|
||||||
|
* @param metadataKey
|
||||||
|
* The metadata key(s) in scope
|
||||||
|
* @returns {MetadataValue[]}
|
||||||
|
* The matching values
|
||||||
|
*/
|
||||||
|
getAllMetadataValue(metadataKey: string): MetadataValue[] {
|
||||||
return Metadata.all(this.metadata, metadataKey);
|
return Metadata.all(this.metadata, metadataKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,17 +15,14 @@
|
|||||||
<div *ngIf="collectionDefaultAccessConditions.length > 0" class="row">
|
<div *ngIf="collectionDefaultAccessConditions.length > 0" class="row">
|
||||||
<div class="col-sm-12" >
|
<div class="col-sm-12" >
|
||||||
<ds-alert [type]="AlertTypeEnum.Warning">
|
<ds-alert [type]="AlertTypeEnum.Warning">
|
||||||
<!-- no def , no banner -->
|
|
||||||
<ng-container *ngIf="collectionPolicyType === 1">
|
<ng-container *ngIf="collectionPolicyType === 1">
|
||||||
<!-- def e no scelta -->
|
|
||||||
{{ 'submission.sections.upload.header.policy.default.nolist' | translate:{ "collectionName": collectionName } }}
|
{{ 'submission.sections.upload.header.policy.default.nolist' | translate:{ "collectionName": collectionName } }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="collectionPolicyType === 2">
|
<ng-container *ngIf="collectionPolicyType === 2">
|
||||||
<!-- def e scelta -->
|
|
||||||
{{ 'submission.sections.upload.header.policy.default.withlist' | translate:{ "collectionName": collectionName } }}
|
{{ 'submission.sections.upload.header.policy.default.withlist' | translate:{ "collectionName": collectionName } }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<span class="clearfix"></span>
|
<span class="clearfix"></span>
|
||||||
<ds-access-conditions [accessConditions]="collectionDefaultAccessConditions"></ds-access-conditions>
|
<ds-submission-section-upload-access-conditions [accessConditions]="collectionDefaultAccessConditions"></ds-submission-section-upload-access-conditions>
|
||||||
</ds-alert>
|
</ds-alert>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -24,7 +24,7 @@ import { BrowserModule } from '@angular/platform-browser';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SubmissionUploadsConfigService } from '../../../core/config/submission-uploads-config.service';
|
import { SubmissionUploadsConfigService } from '../../../core/config/submission-uploads-config.service';
|
||||||
import { SectionUploadService } from './section-upload.service';
|
import { SectionUploadService } from './section-upload.service';
|
||||||
import { UploadSectionComponent } from './section-upload.component';
|
import { SubmissionSectionUploadComponent } from './section-upload.component';
|
||||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { GroupEpersonService } from '../../../core/eperson/group-eperson.service';
|
import { GroupEpersonService } from '../../../core/eperson/group-eperson.service';
|
||||||
import { cold, hot } from 'jasmine-marbles';
|
import { cold, hot } from 'jasmine-marbles';
|
||||||
@@ -71,11 +71,11 @@ const sectionObject: SectionDataObject = {
|
|||||||
sectionType: SectionsType.Upload
|
sectionType: SectionsType.Upload
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('UploadSectionComponent test suite', () => {
|
describe('SubmissionSectionUploadComponent test suite', () => {
|
||||||
|
|
||||||
let comp: UploadSectionComponent;
|
let comp: SubmissionSectionUploadComponent;
|
||||||
let compAsAny: any;
|
let compAsAny: any;
|
||||||
let fixture: ComponentFixture<UploadSectionComponent>;
|
let fixture: ComponentFixture<SubmissionSectionUploadComponent>;
|
||||||
let submissionServiceStub: SubmissionServiceStub;
|
let submissionServiceStub: SubmissionServiceStub;
|
||||||
let sectionsServiceStub: SectionsServiceStub;
|
let sectionsServiceStub: SectionsServiceStub;
|
||||||
let collectionDataService: any;
|
let collectionDataService: any;
|
||||||
@@ -114,7 +114,7 @@ describe('UploadSectionComponent test suite', () => {
|
|||||||
TranslateModule.forRoot()
|
TranslateModule.forRoot()
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
UploadSectionComponent,
|
SubmissionSectionUploadComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -127,7 +127,7 @@ describe('UploadSectionComponent test suite', () => {
|
|||||||
{ provide: 'sectionDataProvider', useValue: sectionObject },
|
{ provide: 'sectionDataProvider', useValue: sectionObject },
|
||||||
{ provide: 'submissionIdProvider', useValue: submissionId },
|
{ provide: 'submissionIdProvider', useValue: submissionId },
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
UploadSectionComponent
|
SubmissionSectionUploadComponent
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents().then();
|
}).compileComponents().then();
|
||||||
@@ -150,7 +150,7 @@ describe('UploadSectionComponent test suite', () => {
|
|||||||
testFixture.destroy();
|
testFixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create UploadSectionComponent', inject([UploadSectionComponent], (app: UploadSectionComponent) => {
|
it('should create SubmissionSectionUploadComponent', inject([SubmissionSectionUploadComponent], (app: SubmissionSectionUploadComponent) => {
|
||||||
|
|
||||||
expect(app).toBeDefined();
|
expect(app).toBeDefined();
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ describe('UploadSectionComponent test suite', () => {
|
|||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(UploadSectionComponent);
|
fixture = TestBed.createComponent(SubmissionSectionUploadComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
compAsAny = comp;
|
compAsAny = comp;
|
||||||
submissionServiceStub = TestBed.get(SubmissionService);
|
submissionServiceStub = TestBed.get(SubmissionService);
|
||||||
@@ -204,15 +204,17 @@ describe('UploadSectionComponent test suite', () => {
|
|||||||
|
|
||||||
comp.onSectionInit();
|
comp.onSectionInit();
|
||||||
|
|
||||||
const expectedGroupsMap = new Map();
|
const expectedGroupsMap = new Map([
|
||||||
expectedGroupsMap.set(mockGroup.id, { name: mockGroup.name, uuid: mockGroup.uuid });
|
[mockUploadConfigResponse.accessConditionOptions[1].name, [mockGroup as any]],
|
||||||
|
[mockUploadConfigResponse.accessConditionOptions[2].name, [mockGroup as any]],
|
||||||
|
]);
|
||||||
|
|
||||||
expect(comp.collectionId).toBe(collectionId);
|
expect(comp.collectionId).toBe(collectionId);
|
||||||
expect(comp.collectionName).toBe(mockCollection.name);
|
expect(comp.collectionName).toBe(mockCollection.name);
|
||||||
expect(comp.availableAccessConditionOptions.length).toBe(4);
|
expect(comp.availableAccessConditionOptions.length).toBe(4);
|
||||||
expect(comp.availableAccessConditionOptions).toEqual(mockUploadConfigResponse.accessConditionOptions as any);
|
expect(comp.availableAccessConditionOptions).toEqual(mockUploadConfigResponse.accessConditionOptions as any);
|
||||||
expect(compAsAny.subs.length).toBe(2);
|
expect(compAsAny.subs.length).toBe(2);
|
||||||
expect(compAsAny.availableGroups.size).toBe(1);
|
expect(compAsAny.availableGroups.size).toBe(2);
|
||||||
expect(compAsAny.availableGroups).toEqual(expectedGroupsMap);
|
expect(compAsAny.availableGroups).toEqual(expectedGroupsMap);
|
||||||
expect(compAsAny.fileList).toEqual([]);
|
expect(compAsAny.fileList).toEqual([]);
|
||||||
expect(compAsAny.fileIndexes).toEqual([]);
|
expect(compAsAny.fileIndexes).toEqual([]);
|
||||||
@@ -248,15 +250,17 @@ describe('UploadSectionComponent test suite', () => {
|
|||||||
|
|
||||||
comp.onSectionInit();
|
comp.onSectionInit();
|
||||||
|
|
||||||
const expectedGroupsMap = new Map();
|
const expectedGroupsMap = new Map([
|
||||||
expectedGroupsMap.set(mockGroup.id, { name: mockGroup.name, uuid: mockGroup.uuid });
|
[mockUploadConfigResponse.accessConditionOptions[1].name, [mockGroup as any]],
|
||||||
|
[mockUploadConfigResponse.accessConditionOptions[2].name, [mockGroup as any]],
|
||||||
|
]);
|
||||||
|
|
||||||
expect(comp.collectionId).toBe(collectionId);
|
expect(comp.collectionId).toBe(collectionId);
|
||||||
expect(comp.collectionName).toBe(mockCollection.name);
|
expect(comp.collectionName).toBe(mockCollection.name);
|
||||||
expect(comp.availableAccessConditionOptions.length).toBe(4);
|
expect(comp.availableAccessConditionOptions.length).toBe(4);
|
||||||
expect(comp.availableAccessConditionOptions).toEqual(mockUploadConfigResponse.accessConditionOptions as any);
|
expect(comp.availableAccessConditionOptions).toEqual(mockUploadConfigResponse.accessConditionOptions as any);
|
||||||
expect(compAsAny.subs.length).toBe(2);
|
expect(compAsAny.subs.length).toBe(2);
|
||||||
expect(compAsAny.availableGroups.size).toBe(1);
|
expect(compAsAny.availableGroups.size).toBe(2);
|
||||||
expect(compAsAny.availableGroups).toEqual(expectedGroupsMap);
|
expect(compAsAny.availableGroups).toEqual(expectedGroupsMap);
|
||||||
expect(compAsAny.fileList).toEqual(mockUploadFiles);
|
expect(compAsAny.fileList).toEqual(mockUploadFiles);
|
||||||
expect(compAsAny.fileIndexes).toEqual(['123456-test-upload']);
|
expect(compAsAny.fileIndexes).toEqual(['123456-test-upload']);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { combineLatest, Observable } from 'rxjs';
|
import { combineLatest, Observable, Subscription } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, find, flatMap, map, reduce, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, find, flatMap, map, reduce, take, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SectionModelComponent } from '../models/section.model';
|
import { SectionModelComponent } from '../models/section.model';
|
||||||
@@ -15,7 +15,7 @@ import { SectionsType } from '../sections-type';
|
|||||||
import { renderSectionFor } from '../sections-decorator';
|
import { renderSectionFor } from '../sections-decorator';
|
||||||
import { SectionDataObject } from '../models/section-data.model';
|
import { SectionDataObject } from '../models/section-data.model';
|
||||||
import { SubmissionObjectEntry } from '../../objects/submission-objects.reducer';
|
import { SubmissionObjectEntry } from '../../objects/submission-objects.reducer';
|
||||||
import { AlertType } from '../../../shared/alerts/aletrs-type';
|
import { AlertType } from '../../../shared/alert/aletr-type';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { Group } from '../../../core/eperson/models/group.model';
|
import { Group } from '../../../core/eperson/models/group.model';
|
||||||
import { SectionsService } from '../sections.service';
|
import { SectionsService } from '../sections.service';
|
||||||
@@ -23,49 +23,105 @@ import { SubmissionService } from '../../submission.service';
|
|||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { ResourcePolicy } from '../../../core/shared/resource-policy.model';
|
import { ResourcePolicy } from '../../../core/shared/resource-policy.model';
|
||||||
import { AccessConditionOption } from '../../../core/config/models/config-access-condition-option.model';
|
import { AccessConditionOption } from '../../../core/config/models/config-access-condition-option.model';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
export interface AccessConditionGroupsMapEntry {
|
||||||
|
accessCondition: string;
|
||||||
|
groups: Group[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component represents a section that contains submission's bitstreams
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-section-upload',
|
selector: 'ds-submission-section-upload',
|
||||||
styleUrls: ['./section-upload.component.scss'],
|
styleUrls: ['./section-upload.component.scss'],
|
||||||
templateUrl: './section-upload.component.html',
|
templateUrl: './section-upload.component.html',
|
||||||
})
|
})
|
||||||
@renderSectionFor(SectionsType.Upload)
|
@renderSectionFor(SectionsType.Upload)
|
||||||
export class UploadSectionComponent extends SectionModelComponent {
|
export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AlertType enumeration
|
||||||
|
* @type {AlertType}
|
||||||
|
*/
|
||||||
public AlertTypeEnum = AlertType;
|
public AlertTypeEnum = AlertType;
|
||||||
public fileIndexes = [];
|
|
||||||
public fileList = [];
|
|
||||||
public fileNames = [];
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array containing the keys of file list array
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
public fileIndexes: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file list
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
public fileList: any[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array containing the name of the files
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
public fileNames: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection name this submission belonging to
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
public collectionName: string;
|
public collectionName: string;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Default access conditions of this collection
|
* Default access conditions of this collection
|
||||||
|
* @type {Array}
|
||||||
*/
|
*/
|
||||||
public collectionDefaultAccessConditions: any[] = [];
|
public collectionDefaultAccessConditions: any[] = [];
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* The collection access conditions policy
|
* Define if collection access conditions policy type :
|
||||||
|
* POLICY_DEFAULT_NO_LIST : is not possible to define additional access group/s for the single file
|
||||||
|
* POLICY_DEFAULT_WITH_LIST : is possible to define additional access group/s for the single file
|
||||||
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
public collectionPolicyType;
|
public collectionPolicyType: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration for the bitstream's metadata form
|
||||||
|
*/
|
||||||
public configMetadataForm$: Observable<SubmissionFormsModel>;
|
public configMetadataForm$: Observable<SubmissionFormsModel>;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* List of available access conditions that could be setted to files
|
* List of available access conditions that could be setted to files
|
||||||
*/
|
*/
|
||||||
public availableAccessConditionOptions: AccessConditionOption[]; // List of accessConditions that an user can select
|
public availableAccessConditionOptions: AccessConditionOption[]; // List of accessConditions that an user can select
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* List of Groups available for every access condition
|
* List of Groups available for every access condition
|
||||||
*/
|
*/
|
||||||
protected availableGroups: Map<string, any>; // Groups for any policy
|
protected availableGroups: Map<string, Group[]>; // Groups for any policy
|
||||||
|
|
||||||
protected subs = [];
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
protected subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {SectionUploadService} bitstreamService
|
||||||
|
* @param {ChangeDetectorRef} changeDetectorRef
|
||||||
|
* @param {CollectionDataService} collectionDataService
|
||||||
|
* @param {GroupEpersonService} groupService
|
||||||
|
* @param {SectionsService} sectionService
|
||||||
|
* @param {SubmissionService} submissionService
|
||||||
|
* @param {SubmissionUploadsConfigService} uploadsConfigService
|
||||||
|
* @param {SectionDataObject} injectedSectionData
|
||||||
|
* @param {string} injectedSubmissionId
|
||||||
|
*/
|
||||||
constructor(private bitstreamService: SectionUploadService,
|
constructor(private bitstreamService: SectionUploadService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private collectionDataService: CollectionDataService,
|
private collectionDataService: CollectionDataService,
|
||||||
@@ -78,10 +134,14 @@ export class UploadSectionComponent extends SectionModelComponent {
|
|||||||
super(undefined, injectedSectionData, injectedSubmissionId);
|
super(undefined, injectedSectionData, injectedSubmissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all instance variables and retrieve collection default access conditions
|
||||||
|
*/
|
||||||
onSectionInit() {
|
onSectionInit() {
|
||||||
const config$ = this.uploadsConfigService.getConfigByHref(this.sectionData.config).pipe(
|
const config$ = this.uploadsConfigService.getConfigByHref(this.sectionData.config).pipe(
|
||||||
map((config) => config.payload));
|
map((config) => config.payload));
|
||||||
|
|
||||||
|
// retrieve configuration for the bitstream's metadata form
|
||||||
this.configMetadataForm$ = config$.pipe(
|
this.configMetadataForm$ = config$.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
map((config: SubmissionUploadsModel) => config.metadata));
|
map((config: SubmissionUploadsModel) => config.metadata));
|
||||||
@@ -117,41 +177,48 @@ export class UploadSectionComponent extends SectionModelComponent {
|
|||||||
: POLICY_DEFAULT_NO_LIST;
|
: POLICY_DEFAULT_NO_LIST;
|
||||||
|
|
||||||
this.availableGroups = new Map();
|
this.availableGroups = new Map();
|
||||||
const groups$ = [];
|
const mapGroups$: Array<Observable<AccessConditionGroupsMapEntry>> = [];
|
||||||
// Retrieve Groups for accessConditionPolicies
|
// Retrieve Groups for accessCondition Policies
|
||||||
this.availableAccessConditionOptions.forEach((accessCondition: AccessConditionOption) => {
|
this.availableAccessConditionOptions.forEach((accessCondition: AccessConditionOption) => {
|
||||||
if (accessCondition.hasEndDate === true || accessCondition.hasStartDate === true) {
|
if (accessCondition.hasEndDate === true || accessCondition.hasStartDate === true) {
|
||||||
groups$.push(
|
if (accessCondition.groupUUID) {
|
||||||
this.groupService.findById(accessCondition.groupUUID).pipe(
|
mapGroups$.push(
|
||||||
find((rd: RemoteData<Group>) => !rd.isResponsePending && rd.hasSucceeded))
|
this.groupService.findById(accessCondition.groupUUID).pipe(
|
||||||
);
|
find((rd: RemoteData<Group>) => !rd.isResponsePending && rd.hasSucceeded),
|
||||||
|
map((rd: RemoteData<Group>) => ({
|
||||||
|
accessCondition: accessCondition.name,
|
||||||
|
groups: [rd.payload]
|
||||||
|
} as AccessConditionGroupsMapEntry)))
|
||||||
|
);
|
||||||
|
} else if (accessCondition.selectGroupUUID) {
|
||||||
|
mapGroups$.push(
|
||||||
|
this.groupService.findById(accessCondition.selectGroupUUID).pipe(
|
||||||
|
find((rd: RemoteData<Group>) => !rd.isResponsePending && rd.hasSucceeded),
|
||||||
|
flatMap((group: RemoteData<Group>) => group.payload.groups),
|
||||||
|
find((rd: RemoteData<PaginatedList<Group>>) => !rd.isResponsePending && rd.hasSucceeded),
|
||||||
|
map((rd: RemoteData<PaginatedList<Group>>) => ({
|
||||||
|
accessCondition: accessCondition.name,
|
||||||
|
groups: rd.payload.page
|
||||||
|
} as AccessConditionGroupsMapEntry))
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return groups$;
|
return mapGroups$;
|
||||||
}),
|
}),
|
||||||
flatMap((group) => group),
|
flatMap((entry) => entry),
|
||||||
reduce((acc: Group[], group: RemoteData<Group>) => {
|
reduce((acc: any[], entry: AccessConditionGroupsMapEntry) => {
|
||||||
acc.push(group.payload);
|
acc.push(entry);
|
||||||
return acc;
|
return acc;
|
||||||
}, []),
|
}, []),
|
||||||
).subscribe((groups: Group[]) => {
|
).subscribe((entries: AccessConditionGroupsMapEntry[]) => {
|
||||||
groups.forEach((group: Group) => {
|
entries.forEach((entry: AccessConditionGroupsMapEntry) => {
|
||||||
if (isUndefined(this.availableGroups.get(group.uuid))) {
|
this.availableGroups.set(entry.accessCondition, entry.groups);
|
||||||
if (Array.isArray(group.groups)) {
|
|
||||||
const groupArrayData = [];
|
|
||||||
for (const groupData of group.groups) {
|
|
||||||
groupArrayData.push({ name: groupData.name, uuid: groupData.uuid });
|
|
||||||
}
|
|
||||||
this.availableGroups.set(group.uuid, groupArrayData);
|
|
||||||
} else {
|
|
||||||
this.availableGroups.set(group.uuid, { name: group.name, uuid: group.uuid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
})
|
}),
|
||||||
,
|
|
||||||
|
// retrieve submission's bitstreams from state
|
||||||
combineLatest(this.configMetadataForm$,
|
combineLatest(this.configMetadataForm$,
|
||||||
this.bitstreamService.getUploadedFileList(this.submissionId, this.sectionData.id)).pipe(
|
this.bitstreamService.getUploadedFileList(this.submissionId, this.sectionData.id)).pipe(
|
||||||
filter(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
|
filter(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
|
||||||
@@ -159,24 +226,32 @@ export class UploadSectionComponent extends SectionModelComponent {
|
|||||||
}),
|
}),
|
||||||
distinctUntilChanged())
|
distinctUntilChanged())
|
||||||
.subscribe(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
|
.subscribe(([configMetadataForm, fileList]: [SubmissionFormsModel, any[]]) => {
|
||||||
this.fileList = [];
|
this.fileList = [];
|
||||||
this.fileIndexes = [];
|
this.fileIndexes = [];
|
||||||
this.fileNames = [];
|
this.fileNames = [];
|
||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
if (isNotUndefined(fileList) && fileList.length > 0) {
|
if (isNotUndefined(fileList) && fileList.length > 0) {
|
||||||
fileList.forEach((file) => {
|
fileList.forEach((file) => {
|
||||||
this.fileList.push(file);
|
this.fileList.push(file);
|
||||||
this.fileIndexes.push(file.uuid);
|
this.fileIndexes.push(file.uuid);
|
||||||
this.fileNames.push(this.getFileName(configMetadataForm, file));
|
this.fileNames.push(this.getFileName(configMetadataForm, file));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return file name from metadata
|
||||||
|
*
|
||||||
|
* @param configMetadataForm
|
||||||
|
* the bitstream's form configuration
|
||||||
|
* @param fileData
|
||||||
|
* the file metadata
|
||||||
|
*/
|
||||||
private getFileName(configMetadataForm: SubmissionFormsModel, fileData: any): string {
|
private getFileName(configMetadataForm: SubmissionFormsModel, fileData: any): string {
|
||||||
const metadataName: string = configMetadataForm.rows[0].fields[0].selectableMetadata[0].metadata;
|
const metadataName: string = configMetadataForm.rows[0].fields[0].selectableMetadata[0].metadata;
|
||||||
let title: string;
|
let title: string;
|
||||||
@@ -189,6 +264,12 @@ export class UploadSectionComponent extends SectionModelComponent {
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get section status
|
||||||
|
*
|
||||||
|
* @return Observable<boolean>
|
||||||
|
* the section status
|
||||||
|
*/
|
||||||
protected getSectionStatus(): Observable<boolean> {
|
protected getSectionStatus(): Observable<boolean> {
|
||||||
return this.bitstreamService.getUploadedFileList(this.submissionId, this.sectionData.id).pipe(
|
return this.bitstreamService.getUploadedFileList(this.submissionId, this.sectionData.id).pipe(
|
||||||
map((fileList: any[]) => (isNotUndefined(fileList) && fileList.length > 0)));
|
map((fileList: any[]) => (isNotUndefined(fileList) && fileList.length > 0)));
|
||||||
|
@@ -14,51 +14,127 @@ import { submissionUploadedFileFromUuidSelector, submissionUploadedFilesFromIdSe
|
|||||||
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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that provides methods to handle submission's bitstream state.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SectionUploadService {
|
export class SectionUploadService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize service variables
|
||||||
|
*
|
||||||
|
* @param {Store<SubmissionState>} store
|
||||||
|
*/
|
||||||
constructor(private store: Store<SubmissionState>) {}
|
constructor(private store: Store<SubmissionState>) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return submission's bitstream list from state
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @returns {Array}
|
||||||
|
* Returns submission's bitstream list
|
||||||
|
*/
|
||||||
public getUploadedFileList(submissionId: string, sectionId: string): Observable<any> {
|
public getUploadedFileList(submissionId: string, sectionId: string): Observable<any> {
|
||||||
return this.store.select(submissionUploadedFilesFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionUploadedFilesFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
map((state) => state),
|
map((state) => state),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFileData(submissionId: string, sectionId: string, fileUuid: string): Observable<any> {
|
/**
|
||||||
|
* Return bitstream's metadata
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @param fileUUID
|
||||||
|
* The bitstream UUID
|
||||||
|
* @returns {Observable}
|
||||||
|
* Emits bitstream's metadata
|
||||||
|
*/
|
||||||
|
public getFileData(submissionId: string, sectionId: string, fileUUID: string): Observable<any> {
|
||||||
return this.store.select(submissionUploadedFilesFromIdSelector(submissionId, sectionId)).pipe(
|
return this.store.select(submissionUploadedFilesFromIdSelector(submissionId, sectionId)).pipe(
|
||||||
filter((state) => !isUndefined(state)),
|
filter((state) => !isUndefined(state)),
|
||||||
map((state) => {
|
map((state) => {
|
||||||
let fileState;
|
let fileState;
|
||||||
Object.keys(state)
|
Object.keys(state)
|
||||||
.filter((key) => state[key].uuid === fileUuid)
|
.filter((key) => state[key].uuid === fileUUID)
|
||||||
.forEach((key) => fileState = state[key]);
|
.forEach((key) => fileState = state[key]);
|
||||||
return fileState;
|
return fileState;
|
||||||
}),
|
}),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDefaultPolicies(submissionId: string, sectionId: string, fileId: string): Observable<any> {
|
/**
|
||||||
return this.store.select(submissionUploadedFileFromUuidSelector(submissionId, sectionId, fileId)).pipe(
|
* Return bitstream's default policies
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @param fileUUID
|
||||||
|
* The bitstream UUID
|
||||||
|
* @returns {Observable}
|
||||||
|
* Emits bitstream's default policies
|
||||||
|
*/
|
||||||
|
public getDefaultPolicies(submissionId: string, sectionId: string, fileUUID: string): Observable<any> {
|
||||||
|
return this.store.select(submissionUploadedFileFromUuidSelector(submissionId, sectionId, fileUUID)).pipe(
|
||||||
map((state) => state),
|
map((state) => state),
|
||||||
distinctUntilChanged());
|
distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
public addUploadedFile(submissionId: string, sectionId: string, fileId: string, data: WorkspaceitemSectionUploadFileObject) {
|
/**
|
||||||
|
* Add a new bitstream to the state
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @param fileUUID
|
||||||
|
* The bitstream UUID
|
||||||
|
* @param data
|
||||||
|
* The [[WorkspaceitemSectionUploadFileObject]] object
|
||||||
|
*/
|
||||||
|
public addUploadedFile(submissionId: string, sectionId: string, fileUUID: string, data: WorkspaceitemSectionUploadFileObject) {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
new NewUploadedFileAction(submissionId, sectionId, fileId, data)
|
new NewUploadedFileAction(submissionId, sectionId, fileUUID, data)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateFileData(submissionId: string, sectionId: string, fileId: string, data: WorkspaceitemSectionUploadFileObject) {
|
/**
|
||||||
|
* Update bitstream metadata into the state
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @param fileUUID
|
||||||
|
* The bitstream UUID
|
||||||
|
* @param data
|
||||||
|
* The [[WorkspaceitemSectionUploadFileObject]] object
|
||||||
|
*/
|
||||||
|
public updateFileData(submissionId: string, sectionId: string, fileUUID: string, data: WorkspaceitemSectionUploadFileObject) {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
new EditFileDataAction(submissionId, sectionId, fileId, data)
|
new EditFileDataAction(submissionId, sectionId, fileUUID, data)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeUploadedFile(submissionId: string, sectionId: string, fileId: string) {
|
/**
|
||||||
|
* Remove bitstream from the state
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
* @param sectionId
|
||||||
|
* The section id
|
||||||
|
* @param fileUUID
|
||||||
|
* The bitstream UUID
|
||||||
|
*/
|
||||||
|
public removeUploadedFile(submissionId: string, sectionId: string, fileUUID: string) {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
new DeleteUploadedFileAction(submissionId, sectionId, fileId)
|
new DeleteUploadedFileAction(submissionId, sectionId, fileUUID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,9 @@ import { hasValue } from '../shared/empty.util';
|
|||||||
import { submissionSelector, SubmissionState } from './submission.reducers';
|
import { submissionSelector, SubmissionState } from './submission.reducers';
|
||||||
import { SubmissionObjectEntry, SubmissionSectionObject } from './objects/submission-objects.reducer';
|
import { SubmissionObjectEntry, SubmissionSectionObject } from './objects/submission-objects.reducer';
|
||||||
|
|
||||||
// @TODO: Merge with keySelector function present in 'src/app/core/shared/selectors.ts'
|
/**
|
||||||
|
* Export a function to return a subset of the state by key
|
||||||
|
*/
|
||||||
export function keySelector<T, V>(parentSelector: Selector<any, any>, subState: string, key: string): MemoizedSelector<T, V> {
|
export function keySelector<T, V>(parentSelector: Selector<any, any>, subState: string, key: string): MemoizedSelector<T, V> {
|
||||||
return createSelector(parentSelector, (state: T) => {
|
return createSelector(parentSelector, (state: T) => {
|
||||||
if (hasValue(state) && hasValue(state[subState])) {
|
if (hasValue(state) && hasValue(state[subState])) {
|
||||||
@@ -15,6 +17,9 @@ export function keySelector<T, V>(parentSelector: Selector<any, any>, subState:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a function to return a subset of the state
|
||||||
|
*/
|
||||||
export function subStateSelector<T, V>(parentSelector: Selector<any, any>, subState: string): MemoizedSelector<T, V> {
|
export function subStateSelector<T, V>(parentSelector: Selector<any, any>, subState: string): MemoizedSelector<T, V> {
|
||||||
return createSelector(parentSelector, (state: T) => {
|
return createSelector(parentSelector, (state: T) => {
|
||||||
if (hasValue(state) && hasValue(state[subState])) {
|
if (hasValue(state) && hasValue(state[subState])) {
|
||||||
|
@@ -6,21 +6,45 @@ import { SubmissionService } from './submission.service';
|
|||||||
import { SubmissionObject } from '../core/submission/models/submission-object.model';
|
import { SubmissionObject } from '../core/submission/models/submission-object.model';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of SubmissionService used on SSR.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerSubmissionService extends SubmissionService {
|
export class ServerSubmissionService extends SubmissionService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override createSubmission parent method to return an empty observable
|
||||||
|
*
|
||||||
|
* @return Observable<SubmissionObject>
|
||||||
|
* observable of SubmissionObject
|
||||||
|
*/
|
||||||
createSubmission(): Observable<SubmissionObject> {
|
createSubmission(): Observable<SubmissionObject> {
|
||||||
return observableOf(null);
|
return observableOf(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override retrieveSubmission parent method to return an empty observable
|
||||||
|
*
|
||||||
|
* @return Observable<SubmissionObject>
|
||||||
|
* observable of SubmissionObject
|
||||||
|
*/
|
||||||
retrieveSubmission(submissionId): Observable<RemoteData<SubmissionObject>> {
|
retrieveSubmission(submissionId): Observable<RemoteData<SubmissionObject>> {
|
||||||
return observableOf(null);
|
return observableOf(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override startAutoSave parent method and return without doing anything
|
||||||
|
*
|
||||||
|
* @param submissionId
|
||||||
|
* The submission id
|
||||||
|
*/
|
||||||
startAutoSave(submissionId) {
|
startAutoSave(submissionId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override startAutoSave parent method and return without doing anything
|
||||||
|
*/
|
||||||
stopAutoSave() {
|
stopAutoSave() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user