diff --git a/docker/local.cfg b/docker/local.cfg index 6692b13658..947b3c8248 100644 --- a/docker/local.cfg +++ b/docker/local.cfg @@ -1,6 +1,6 @@ dspace.dir=/dspace db.url=jdbc:postgresql://dspacedb:5432/dspace dspace.hostname=dspace -dspace.baseUrl=http://localhost:8080 +dspace.baseUrl=http://localhost:8080/server dspace.name=DSpace Started with Docker Compose solr.server=http://dspacesolr:8983/solr diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 80847f5101..75617b8bcf 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -161,6 +161,7 @@ "collection.page.browse.recent.head": "Recent Submissions", "collection.page.browse.recent.empty": "No items to show", + "collection.page.handle": "Permanent URI for this collection", "collection.page.license": "License", "collection.page.news": "News", @@ -184,8 +185,10 @@ "community.form.rights": "Copyright text (HTML)", "community.form.tableofcontents": "News (HTML)", "community.form.title": "Name", + "community.page.handle": "Permanent URI for this community", "community.page.license": "License", "community.page.news": "News", + "community.all-lists.head": "Subcommunities and Collections", "community.sub-collection-list.head": "Collections of this Community", "community.sub-community-list.head": "Communities of this Community", diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts index 78f5d52511..a507e8e585 100644 --- a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts @@ -19,6 +19,7 @@ import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config'; import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; import { toRemoteData } from '../+browse-by-metadata-page/browse-by-metadata-page.component.spec'; import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils'; +import { VarDirective } from '../../shared/utils/var.directive'; describe('BrowseByDatePageComponent', () => { let comp: BrowseByDatePageComponent; @@ -69,7 +70,7 @@ describe('BrowseByDatePageComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], - declarations: [BrowseByDatePageComponent, EnumKeysPipe], + declarations: [BrowseByDatePageComponent, EnumKeysPipe, VarDirective], providers: [ { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, { provide: ActivatedRoute, useValue: activatedRouteStub }, diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index c589c543d4..45f2ef3b2a 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -1,7 +1,31 @@
-
+ + +
+ + + + + + + + + + + + +
+ + +
+ +
+ +
+ + +
+
+ + + + +
+
+
+
diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.spec.ts b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.spec.ts index 927effd303..553bd00f56 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.spec.ts +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.spec.ts @@ -23,6 +23,7 @@ import { MockRouter } from '../../shared/mocks/mock-router'; import { ResourceType } from '../../core/shared/resource-type'; import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils'; import { BrowseEntry } from '../../core/shared/browse-entry.model'; +import { VarDirective } from '../../shared/utils/var.directive'; describe('BrowseByMetadataPageComponent', () => { let comp: BrowseByMetadataPageComponent; @@ -86,7 +87,7 @@ describe('BrowseByMetadataPageComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], - declarations: [BrowseByMetadataPageComponent, EnumKeysPipe], + declarations: [BrowseByMetadataPageComponent, EnumKeysPipe, VarDirective], providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: BrowseService, useValue: mockBrowseService }, diff --git a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.spec.ts b/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.spec.ts index 3bc69e5fcb..90623eb3c7 100644 --- a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.spec.ts +++ b/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.spec.ts @@ -18,6 +18,7 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv import { BrowseService } from '../../core/browse/browse.service'; import { MockRouter } from '../../shared/mocks/mock-router'; import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils'; +import { VarDirective } from '../../shared/utils/var.directive'; describe('BrowseByTitlePageComponent', () => { let comp: BrowseByTitlePageComponent; @@ -64,7 +65,7 @@ describe('BrowseByTitlePageComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], - declarations: [BrowseByTitlePageComponent, EnumKeysPipe], + declarations: [BrowseByTitlePageComponent, EnumKeysPipe, VarDirective], providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: BrowseService, useValue: mockBrowseService }, diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html index 2b16bc1ca6..436cd351a0 100644 --- a/src/app/+collection-page/collection-page.component.html +++ b/src/app/+collection-page/collection-page.component.html @@ -3,18 +3,22 @@ *ngVar="(collectionRD$ | async) as collectionRD">
+
+ + + [alternateText]="'Collection Logo'"> + - - - - - - + + + + @@ -23,23 +27,20 @@ + [title]="'collection.page.news'"> - - - - - - -
-
+ + +
+ + + + -
-

{{'collection.page.browse.recent.head' | translate}}

+
+

{{'collection.page.browse.recent.head' | translate}}

-
- - +
+ +
+ + + diff --git a/src/app/+community-page/community-page.component.html b/src/app/+community-page/community-page.component.html index e429d224f2..05d0bd1d0e 100644 --- a/src/app/+community-page/community-page.component.html +++ b/src/app/+community-page/community-page.component.html @@ -1,33 +1,38 @@
- - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + +
diff --git a/src/app/core/submission/submission-object-data.service.spec.ts b/src/app/core/submission/submission-object-data.service.spec.ts new file mode 100644 index 0000000000..b7c06272e6 --- /dev/null +++ b/src/app/core/submission/submission-object-data.service.spec.ts @@ -0,0 +1,95 @@ +import { Observable } from 'rxjs'; +import { SubmissionService } from '../../submission/submission.service'; +import { RemoteData } from '../data/remote-data'; +import { SubmissionObject } from './models/submission-object.model'; +import { WorkspaceItem } from './models/workspaceitem.model'; +import { SubmissionObjectDataService } from './submission-object-data.service'; +import { SubmissionScopeType } from './submission-scope-type'; +import { WorkflowItemDataService } from './workflowitem-data.service'; +import { WorkspaceitemDataService } from './workspaceitem-data.service'; + +describe('SubmissionObjectDataService', () => { + let service: SubmissionObjectDataService; + let submissionService: SubmissionService; + let workspaceitemDataService: WorkspaceitemDataService; + let workflowItemDataService: WorkflowItemDataService; + + const submissionId = '1234'; + const wsiResult = 'wsiResult' as any; + const wfiResult = 'wfiResult' as any; + + beforeEach(() => { + workspaceitemDataService = jasmine.createSpyObj('WorkspaceitemDataService', { + findById: wsiResult + }); + workflowItemDataService = jasmine.createSpyObj('WorkflowItemDataService', { + findById: wfiResult + }); + }); + + describe('findById', () => { + it('should call SubmissionService.getSubmissionScope to determine the type of submission object', () => { + submissionService = jasmine.createSpyObj('SubmissionService', { + getSubmissionScope: {} + }); + service = new SubmissionObjectDataService(workspaceitemDataService, workflowItemDataService, submissionService); + service.findById(submissionId); + expect(submissionService.getSubmissionScope).toHaveBeenCalled(); + }); + + describe('when the submission ID refers to a WorkspaceItem', () => { + beforeEach(() => { + submissionService = jasmine.createSpyObj('SubmissionService', { + getSubmissionScope: SubmissionScopeType.WorkspaceItem + }); + service = new SubmissionObjectDataService(workspaceitemDataService, workflowItemDataService, submissionService); + }); + + it('should forward the result of WorkspaceitemDataService.findById()', () => { + const result = service.findById(submissionId); + expect(workspaceitemDataService.findById).toHaveBeenCalledWith(submissionId); + expect(result).toBe(wsiResult); + }); + }); + + describe('when the submission ID refers to a WorkflowItem', () => { + beforeEach(() => { + submissionService = jasmine.createSpyObj('SubmissionService', { + getSubmissionScope: SubmissionScopeType.WorkflowItem + }); + service = new SubmissionObjectDataService(workspaceitemDataService, workflowItemDataService, submissionService); + }); + + it('should forward the result of WorkflowItemDataService.findById()', () => { + const result = service.findById(submissionId); + expect(workflowItemDataService.findById).toHaveBeenCalledWith(submissionId); + expect(result).toBe(wfiResult); + }); + }); + + describe('when the type of submission object is unknown', () => { + beforeEach(() => { + submissionService = jasmine.createSpyObj('SubmissionService', { + getSubmissionScope: 'Something else' + }); + service = new SubmissionObjectDataService(workspaceitemDataService, workflowItemDataService, submissionService); + }); + + it('shouldn\'t call any data service methods', () => { + service.findById(submissionId); + expect(workspaceitemDataService.findById).not.toHaveBeenCalled(); + expect(workflowItemDataService.findById).not.toHaveBeenCalled(); + }); + + it('should return a RemoteData containing an error', (done) => { + const result = service.findById(submissionId); + result.subscribe((rd: RemoteData) => { + expect(rd.hasFailed).toBe(true); + expect(rd.error).toBeDefined(); + done(); + }) + }); + }); + + }); +}); diff --git a/src/app/core/submission/submission-object-data.service.ts b/src/app/core/submission/submission-object-data.service.ts new file mode 100644 index 0000000000..15ede18cb8 --- /dev/null +++ b/src/app/core/submission/submission-object-data.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { of as observableOf, Observable } from 'rxjs'; +import { SubmissionService } from '../../submission/submission.service'; +import { RemoteData } from '../data/remote-data'; +import { RemoteDataError } from '../data/remote-data-error'; +import { SubmissionObject } from './models/submission-object.model'; +import { SubmissionScopeType } from './submission-scope-type'; +import { WorkflowItemDataService } from './workflowitem-data.service'; +import { WorkspaceitemDataService } from './workspaceitem-data.service'; + +/** + * A service to retrieve submission objects (WorkspaceItem/WorkflowItem) + * without knowing their type + */ +@Injectable({ + providedIn: 'root' +}) +export class SubmissionObjectDataService { + constructor( + private workspaceitemDataService: WorkspaceitemDataService, + private workflowItemDataService: WorkflowItemDataService, + private submissionService: SubmissionService + ) { + } + + /** + * Retrieve a submission object based on its ID. + * + * @param id The identifier of a submission object + */ + findById(id: string): Observable> { + switch (this.submissionService.getSubmissionScope()) { + case SubmissionScopeType.WorkspaceItem: + return this.workspaceitemDataService.findById(id); + case SubmissionScopeType.WorkflowItem: + return this.workflowItemDataService.findById(id); + default: + const error = new RemoteDataError( + undefined, + undefined, + 'The request couldn\'t be sent. Unable to determine the type of submission object' + ); + return observableOf(new RemoteData(false, false, false, error, undefined)); + } + } +} diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index f7cd7e0d35..09d3cd3d60 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -1,5 +1,5 @@ -

{{title | translate}}

+

{{title | translate}}

diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 6c4bc78213..230b0bc136 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -26,6 +26,10 @@ export class BrowseByComponent implements OnInit { */ @Input() title: string; + /** + * The parent name + */ + @Input() parentname: string; /** * The list of objects to display */ diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html index 1c73fbb3df..504d9f4bcd 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html @@ -1,6 +1,24 @@ -

{{'browse.comcol.head' | translate}}

- +

{{'browse.comcol.head' | translate}}

+ diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.scss b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts index dcc7840bb4..1bc83d74a5 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -1,6 +1,27 @@ -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Inject, + Input, NgZone, + OnDestroy, + OnInit +} from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { Subscription } from 'rxjs/internal/Subscription'; +import { filter, map, startWith, tap } from 'rxjs/operators'; +import { getCollectionPageRoute } from '../../+collection-page/collection-page-routing.module'; +import { getCommunityPageRoute } from '../../+community-page/community-page-routing.module'; import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { Router, ActivatedRoute, RouterModule, UrlSegment } from '@angular/router'; import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; +import { hasValue } from '../empty.util'; + +export interface ComColPageNavOption { + id: string; + label: string, + routerLink: string + params?: any; +}; /** * A component to display the "Browse By" section of a Community or Collection page @@ -8,24 +29,63 @@ import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interf */ @Component({ selector: 'ds-comcol-page-browse-by', - templateUrl: './comcol-page-browse-by.component.html', + styleUrls: ['./comcol-page-browse-by.component.scss'], + templateUrl: './comcol-page-browse-by.component.html' }) export class ComcolPageBrowseByComponent implements OnInit { /** * The ID of the Community or Collection */ @Input() id: string; - + @Input() contentType: string; /** * List of currently active browse configurations */ types: BrowseByTypeConfig[]; - constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig) { + allOptions: ComColPageNavOption[]; + + currentOptionId$: Observable; + + constructor( + @Inject(GLOBAL_CONFIG) public config: GlobalConfig, + private route: ActivatedRoute, + private router: Router) { } ngOnInit(): void { - this.types = this.config.browseBy.types; + this.allOptions = this.config.browseBy.types + .map((config: BrowseByTypeConfig) => ({ + id: config.id, + label: `browse.comcol.by.${config.id}`, + routerLink: `/browse/${config.id}`, + params: { scope: this.id } + })); + + if (this.contentType === 'collection') { + this.allOptions = [ { + id: this.id, + label: 'collection.page.browse.recent.head', + routerLink: getCollectionPageRoute(this.id) + }, ...this.allOptions ]; + } else if (this.contentType === 'community') { + this.allOptions = [{ + id: this.id, + label: 'community.all-lists.head', + routerLink: getCommunityPageRoute(this.id) + }, ...this.allOptions ]; + } + + this.currentOptionId$ = this.route.url.pipe( + filter((urlSegments: UrlSegment[]) => hasValue(urlSegments)), + map((urlSegments: UrlSegment[]) => urlSegments[urlSegments.length - 1].path) + ); } + onSelectChange(newId: string) { + const selectedOption = this.allOptions + .find((option: ComColPageNavOption) => option.id === newId); + + this.router.navigate([selectedOption.routerLink], { queryParams: selectedOption.params }); + } } diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.html b/src/app/shared/comcol-page-handle/comcol-page-handle.component.html new file mode 100644 index 0000000000..b3ca75bf94 --- /dev/null +++ b/src/app/shared/comcol-page-handle/comcol-page-handle.component.html @@ -0,0 +1,4 @@ +
+

{{ title | translate }}

+ +
diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.scss b/src/app/shared/comcol-page-handle/comcol-page-handle.component.scss new file mode 100644 index 0000000000..5d7bac26c7 --- /dev/null +++ b/src/app/shared/comcol-page-handle/comcol-page-handle.component.scss @@ -0,0 +1,5 @@ +div { + word-break: break-word; + word-wrap: break-word; + overflow-wrap: break-word; +} diff --git a/src/app/shared/comcol-page-handle/comcol-page-handle.component.ts b/src/app/shared/comcol-page-handle/comcol-page-handle.component.ts new file mode 100644 index 0000000000..3a2ab307be --- /dev/null +++ b/src/app/shared/comcol-page-handle/comcol-page-handle.component.ts @@ -0,0 +1,29 @@ +import { Component, Input, Inject, Injectable } from '@angular/core'; +import { GlobalConfig } from '../../../config/global-config.interface'; +import { GLOBAL_CONFIG } from '../../../config'; +import { UIURLCombiner } from '../../core/url-combiner/ui-url-combiner'; +/** + * This component builds a URL from the value of "handle" + */ + +@Component({ + selector: 'ds-comcol-page-handle', + styleUrls: ['./comcol-page-handle.component.scss'], + templateUrl: './comcol-page-handle.component.html' +}) + +@Injectable() +export class ComcolPageHandleComponent { + + // Optional title + @Input() title: string; + + // The value of "handle" + @Input() content: string; + + constructor(@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig) { + } + public getHandle(): string { + return new UIURLCombiner(this.EnvConfig, '/handle/', this.content).toString(); + } +} diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts index f598e142d9..76fe6909d3 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts @@ -115,6 +115,7 @@ describe('DsDynamicFormControlContainerComponent test suite', () => { repeatable: false }), new DynamicRelationGroupModel({ + submissionId: '1234', id: 'relationGroup', formConfiguration: [], mandatoryField: '', diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts index 664c72035d..e254967865 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts @@ -26,7 +26,6 @@ import { MOCK_SUBMISSION_CONFIG } from '../../../../../testing/mock-submission-c import { Store, StoreModule } from '@ngrx/store'; import { MockStore } from '../../../../../testing/mock-store'; import { FormRowModel } from '../../../../../../core/config/models/config-submission-form.model'; -import { WorkspaceItem } from '../../../../../../core/submission/models/workspaceitem.model'; export let FORM_GROUP_TEST_MODEL_CONFIG; @@ -34,6 +33,8 @@ export let FORM_GROUP_TEST_GROUP; const config: GlobalConfig = MOCK_SUBMISSION_CONFIG; +const submissionId = '1234'; + function init() { FORM_GROUP_TEST_MODEL_CONFIG = { disabled: false, @@ -68,6 +69,7 @@ function init() { }] } as FormFieldModel] } as FormRowModel], + submissionId, id: 'dc_contributor_author', label: 'Authors', mandatoryField: 'dc.contributor.author', @@ -79,7 +81,6 @@ function init() { scopeUUID: '43fe1f8c-09a6-4fcf-9c78-5d4fed8f2c8f', submissionScope: undefined, validators: { required: null }, - workspaceItem: new WorkspaceItem(), repeatable: false } as DynamicRelationGroupModelConfig; @@ -186,7 +187,7 @@ describe('DsDynamicRelationGroupComponent test suite', () => { it('should init component properly', inject([FormBuilderService], (service: FormBuilderService) => { const formConfig = { rows: groupComp.model.formConfiguration } as SubmissionFormsModel; - const formModel = service.modelFromConfiguration(formConfig, groupComp.model.scopeUUID, {}, new WorkspaceItem(), groupComp.model.submissionScope, groupComp.model.readOnly); + const formModel = service.modelFromConfiguration(submissionId, formConfig, groupComp.model.scopeUUID, {}, groupComp.model.submissionScope, groupComp.model.readOnly); const chips = new Chips([], 'value', 'dc.contributor.author'); groupComp.formCollapsed.subscribe((value) => { expect(value).toEqual(false); @@ -260,7 +261,7 @@ describe('DsDynamicRelationGroupComponent test suite', () => { it('should init component properly', inject([FormBuilderService], (service: FormBuilderService) => { const formConfig = { rows: groupComp.model.formConfiguration } as SubmissionFormsModel; - const formModel = service.modelFromConfiguration(formConfig, groupComp.model.scopeUUID, {}, new WorkspaceItem(), groupComp.model.submissionScope, groupComp.model.readOnly); + const formModel = service.modelFromConfiguration(submissionId, formConfig, groupComp.model.scopeUUID, {}, groupComp.model.submissionScope, groupComp.model.readOnly); const chips = new Chips(modelValue, 'value', 'dc.contributor.author'); groupComp.formCollapsed.subscribe((value) => { expect(value).toEqual(true); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts index 6427e6e939..8a74935ca0 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts @@ -93,6 +93,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent this.formId = this.formService.getUniqueId(this.model.id); this.formModel = this.formBuilderService.modelFromConfiguration( + this.model.submissionId, config, this.model.scopeUUID, {}, diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts index e6d2b95afc..c1f76f0431 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts @@ -10,6 +10,7 @@ export const PLACEHOLDER_PARENT_METADATA = '#PLACEHOLDER_PARENT_METADATA_VALUE#' * Dynamic Group Model configuration interface */ export interface DynamicRelationGroupModelConfig extends DsDynamicInputModelConfig { + submissionId: string, formConfiguration: FormRowModel[], mandatoryField: string, relationFields: string[], @@ -21,6 +22,7 @@ export interface DynamicRelationGroupModelConfig extends DsDynamicInputModelConf * Dynamic Group Model class */ export class DynamicRelationGroupModel extends DsDynamicInputModel { + @serializable() submissionId: string; @serializable() formConfiguration: FormRowModel[]; @serializable() mandatoryField: string; @serializable() relationFields: string[]; @@ -32,6 +34,7 @@ export class DynamicRelationGroupModel extends DsDynamicInputModel { constructor(config: DynamicRelationGroupModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); + this.submissionId = config.submissionId; this.formConfiguration = config.formConfiguration; this.mandatoryField = config.mandatoryField; this.relationFields = config.relationFields; diff --git a/src/app/shared/form/builder/form-builder.service.spec.ts b/src/app/shared/form/builder/form-builder.service.spec.ts index 8b91fd3eaf..b001b4b95f 100644 --- a/src/app/shared/form/builder/form-builder.service.spec.ts +++ b/src/app/shared/form/builder/form-builder.service.spec.ts @@ -49,7 +49,6 @@ import { DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-conca import { DynamicLookupNameModel } from './ds-dynamic-form-ui/models/lookup/dynamic-lookup-name.model'; import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { FormRowModel } from '../../../core/config/models/config-submission-form.model'; -import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model'; describe('FormBuilderService test suite', () => { @@ -57,6 +56,8 @@ describe('FormBuilderService test suite', () => { let testFormConfiguration: SubmissionFormsModel; let service: FormBuilderService; + const submissionId = '1234'; + function testValidator() { return {testValidator: {valid: true}}; } @@ -194,17 +195,18 @@ describe('FormBuilderService test suite', () => { new DynamicColorPickerModel({id: 'testColorPicker'}), - new DynamicTypeaheadModel({id: 'testTypeahead', workspaceItem: new WorkspaceItem(), repeatable: false}), + new DynamicTypeaheadModel({id: 'testTypeahead', repeatable: false}), - new DynamicScrollableDropdownModel({id: 'testScrollableDropdown', authorityOptions: authorityOptions, workspaceItem: new WorkspaceItem(), repeatable: false}), + new DynamicScrollableDropdownModel({id: 'testScrollableDropdown', authorityOptions: authorityOptions, repeatable: false}), - new DynamicTagModel({id: 'testTag', workspaceItem: new WorkspaceItem(), repeatable: false}), + new DynamicTagModel({id: 'testTag', repeatable: false}), new DynamicListCheckboxGroupModel({id: 'testCheckboxList', authorityOptions: authorityOptions, repeatable: true}), new DynamicListRadioGroupModel({id: 'testRadioList', authorityOptions: authorityOptions, repeatable: false}), new DynamicRelationGroupModel({ + submissionId, id: 'testRelationGroup', formConfiguration: [{ fields: [{ @@ -241,15 +243,14 @@ describe('FormBuilderService test suite', () => { relationFields: [], scopeUUID: '', submissionScope: '', - workspaceItem: new WorkspaceItem(), repeatable: false }), new DynamicDsDatePickerModel({id: 'testDate'}), - new DynamicLookupModel({id: 'testLookup', workspaceItem: new WorkspaceItem(), repeatable: false}), + new DynamicLookupModel({id: 'testLookup', repeatable: false}), - new DynamicLookupNameModel({id: 'testLookupName', workspaceItem: new WorkspaceItem(), repeatable: false}), + new DynamicLookupNameModel({id: 'testLookupName', repeatable: false}), new DynamicQualdropModel({id: 'testCombobox', readOnly: false, required: false}), @@ -410,7 +411,7 @@ describe('FormBuilderService test suite', () => { }); it('should create an array of form models', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); expect(formModel[0] instanceof DynamicRowGroupModel).toBe(true); expect((formModel[0] as DynamicRowGroupModel).group.length).toBe(3); @@ -431,7 +432,7 @@ describe('FormBuilderService test suite', () => { }); it('should return form\'s fields value from form model', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); let value = {} as any; expect(service.getValueFromModel(formModel)).toEqual(value); @@ -452,7 +453,7 @@ describe('FormBuilderService test suite', () => { }); it('should clear all form\'s fields value', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); const value = {} as any; ((formModel[0] as DynamicRowGroupModel).get(1) as DsDynamicInputModel).valueUpdates.next('test'); @@ -464,7 +465,7 @@ describe('FormBuilderService test suite', () => { }); it('should return true when model has a custom group model as parent', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); let model = service.findById('dc_identifier_QUALDROP_VALUE', formModel); let modelParent = service.findById('dc_identifier_QUALDROP_GROUP', formModel); model.parent = modelParent; @@ -493,7 +494,7 @@ describe('FormBuilderService test suite', () => { }); it('should return true when model value is a map', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); const model = service.findById('dc_identifier_QUALDROP_VALUE', formModel); const modelParent = service.findById('dc_identifier_QUALDROP_GROUP', formModel); model.parent = modelParent; @@ -502,7 +503,7 @@ describe('FormBuilderService test suite', () => { }); it('should return true when model is a Qualdrop Group', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); let model = service.findById('dc_identifier_QUALDROP_GROUP', formModel); expect(service.isQualdropGroup(model)).toBe(true); @@ -513,7 +514,7 @@ describe('FormBuilderService test suite', () => { }); it('should return true when model is a Custom or List Group', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); let model = service.findById('dc_identifier_QUALDROP_GROUP', formModel); expect(service.isCustomOrListGroup(model)).toBe(true); @@ -532,7 +533,7 @@ describe('FormBuilderService test suite', () => { }); it('should return true when model is a Custom Group', () => { - const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID', {}, new WorkspaceItem()); + const formModel = service.modelFromConfiguration(submissionId, testFormConfiguration, 'testScopeUUID'); let model = service.findById('dc_identifier_QUALDROP_GROUP', formModel); expect(service.isCustomGroup(model)).toBe(true); diff --git a/src/app/shared/form/builder/form-builder.service.ts b/src/app/shared/form/builder/form-builder.service.ts index 82317e02a7..dcc9403d9b 100644 --- a/src/app/shared/form/builder/form-builder.service.ts +++ b/src/app/shared/form/builder/form-builder.service.ts @@ -11,6 +11,7 @@ import { DynamicFormControlModel, DynamicFormGroupModel, DynamicFormService, + DynamicFormValidationService, DynamicPathable, JSONUtils, } from '@ng-dynamic-forms/core'; @@ -26,11 +27,17 @@ import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model'; import { isNgbDateStruct } from '../../date.util'; -import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model'; @Injectable() export class FormBuilderService extends DynamicFormService { + constructor( + validationService: DynamicFormValidationService, + protected rowParser: RowParser + ) { + super(validationService); + } + findById(id: string, groupModel: DynamicFormControlModel[], arrayIndex = null): DynamicFormControlModel | null { let result = null; @@ -196,13 +203,13 @@ export class FormBuilderService extends DynamicFormService { return result; } - modelFromConfiguration(json: string | SubmissionFormsModel, scopeUUID: string, initFormValues: any = {}, wsi: WorkspaceItem, submissionScope?: string, readOnly = false): DynamicFormControlModel[] | never { + modelFromConfiguration(submissionId: string, json: string | SubmissionFormsModel, scopeUUID: string, sectionData: any = {}, submissionScope?: string, readOnly = false): DynamicFormControlModel[] | never { let rows: DynamicFormControlModel[] = []; const rawData = typeof json === 'string' ? JSON.parse(json, JSONUtils.parseReviver) : json; if (rawData.rows && !isEmpty(rawData.rows)) { rawData.rows.forEach((currentRow) => { - const rowParsed = new RowParser(currentRow, scopeUUID, initFormValues, wsi, submissionScope, readOnly).parse(); + const rowParsed = this.rowParser.parse(submissionId, currentRow, scopeUUID, sectionData, submissionScope, readOnly); if (isNotNull(rowParsed)) { if (Array.isArray(rowParsed)) { rows = rows.concat(rowParsed); diff --git a/src/app/shared/form/builder/parsers/concat-field-parser.ts b/src/app/shared/form/builder/parsers/concat-field-parser.ts index 57bdc8d0dc..33a92c726d 100644 --- a/src/app/shared/form/builder/parsers/concat-field-parser.ts +++ b/src/app/shared/form/builder/parsers/concat-field-parser.ts @@ -1,4 +1,4 @@ -import { FieldParser } from './field-parser'; +import { Inject } from '@angular/core'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { @@ -15,18 +15,25 @@ import { } from '../ds-dynamic-form-ui/models/ds-dynamic-concat.model'; import { isNotEmpty } from '../../../empty.util'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; +import { + CONFIG_DATA, + FieldParser, + INIT_FORM_VALUES, + PARSER_OPTIONS, + SUBMISSION_ID +} from './field-parser'; export class ConcatFieldParser extends FieldParser { - constructor(protected configData: FormFieldModel, - protected initFormValues, - protected parserOptions: ParserOptions, - protected separator: string, - protected workspaceItem: WorkspaceItem, - protected firstPlaceholder: string = null, - protected secondPlaceholder: string = null) { - super(configData, initFormValues, parserOptions, workspaceItem); + constructor( + @Inject(SUBMISSION_ID) submissionId: string, + @Inject(CONFIG_DATA) configData: FormFieldModel, + @Inject(INIT_FORM_VALUES) initFormValues, + @Inject(PARSER_OPTIONS) parserOptions: ParserOptions, + protected separator: string, + protected firstPlaceholder: string = null, + protected secondPlaceholder: string = null) { + super(submissionId, configData, initFormValues, parserOptions); this.separator = separator; this.firstPlaceholder = firstPlaceholder; diff --git a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts index a0b550d244..efa4f3cdb5 100644 --- a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts @@ -3,12 +3,12 @@ import { DateFieldParser } from './date-field-parser'; import { DynamicDsDatePickerModel } from '../ds-dynamic-form-ui/models/date-picker/date-picker.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('DateFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, @@ -36,13 +36,13 @@ describe('DateFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new DateFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof DateFieldParser).toBe(true); }); it('should return a DynamicDsDatePickerModel object when repeatable option is false', () => { - const parser = new DateFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -55,7 +55,7 @@ describe('DateFieldParser test suite', () => { }; const expectedValue = '1983-11-18'; - const parser = new DateFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts index c42a80cb57..8dbd68e05a 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts @@ -2,11 +2,11 @@ import { FormFieldModel } from '../models/form-field.model'; import { DropdownFieldParser } from './dropdown-field-parser'; import { DynamicScrollableDropdownModel } from '../ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('DropdownFieldParser test suite', () => { let field: FormFieldModel; + const submissionId = '1234'; const initFormValues = {}; const parserOptions: ParserOptions = { readOnly: false, @@ -36,13 +36,13 @@ describe('DropdownFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new DropdownFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof DropdownFieldParser).toBe(true); }); it('should return a DynamicScrollableDropdownModel object when repeatable option is false', () => { - const parser = new DropdownFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -51,7 +51,7 @@ describe('DropdownFieldParser test suite', () => { it('should throw when authority is not passed', () => { field.selectableMetadata[0].authority = null; - const parser = new DropdownFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); expect(() => parser.parse()) .toThrow(); diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts index 1623829b15..4816a2a073 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts @@ -1,4 +1,12 @@ -import { FieldParser } from './field-parser'; +import { Inject } from '@angular/core'; +import { FormFieldModel } from '../models/form-field.model'; +import { + CONFIG_DATA, + FieldParser, + INIT_FORM_VALUES, + PARSER_OPTIONS, + SUBMISSION_ID +} from './field-parser'; import { DynamicFormControlLayout, } from '@ng-dynamic-forms/core'; import { DynamicScrollableDropdownModel, @@ -6,9 +14,19 @@ import { } from '../ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { isNotEmpty } from '../../../empty.util'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; +import { ParserOptions } from './parser-options'; export class DropdownFieldParser extends FieldParser { + constructor( + @Inject(SUBMISSION_ID) submissionId: string, + @Inject(CONFIG_DATA) configData: FormFieldModel, + @Inject(INIT_FORM_VALUES) initFormValues, + @Inject(PARSER_OPTIONS) parserOptions: ParserOptions + ) { + super(submissionId, configData, initFormValues, parserOptions) + } + public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { const dropdownModelConfig: DynamicScrollableDropdownModelConfig = this.initModel(null, label); let layout: DynamicFormControlLayout; diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index 9092445905..6fbcef91ea 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -1,4 +1,5 @@ -import { hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../../empty.util'; +import { Inject, InjectionToken } from '@angular/core'; +import { hasValue, isNotEmpty, isNotNull, isNotUndefined, isEmpty } from '../../../empty.util'; import { FormFieldModel } from '../models/form-field.model'; import { uniqueId } from 'lodash'; @@ -12,13 +13,22 @@ import { DynamicFormControlLayout } from '@ng-dynamic-forms/core'; import { setLayout } from './parser.utils'; import { AuthorityOptions } from '../../../../core/integration/models/authority-options.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; + +export const SUBMISSION_ID: InjectionToken = new InjectionToken('submissionId'); +export const CONFIG_DATA: InjectionToken = new InjectionToken('configData'); +export const INIT_FORM_VALUES:InjectionToken = new InjectionToken('initFormValues'); +export const PARSER_OPTIONS: InjectionToken = new InjectionToken('parserOptions'); export abstract class FieldParser { protected fieldId: string; - constructor(protected configData: FormFieldModel, protected initFormValues, protected parserOptions: ParserOptions, protected workspaceItem: WorkspaceItem) { - } + + constructor( + @Inject(SUBMISSION_ID) protected submissionId: string, + @Inject(CONFIG_DATA) protected configData: FormFieldModel, + @Inject(INIT_FORM_VALUES) protected initFormValues: any, + @Inject(PARSER_OPTIONS) protected parserOptions: ParserOptions + ) {} public abstract modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any; @@ -31,11 +41,12 @@ export abstract class FieldParser { ) { let arrayCounter = 0; let fieldArrayCounter = 0; + const config = { id: uniqueId() + '_array', + label: this.configData.label, initialCount: this.getInitArrayIndex(), notRepeatable: !this.configData.repeatable, - label: this.configData.label, required: isNotEmpty(this.configData.mandatory), groupFactory: () => { let model; @@ -185,7 +196,6 @@ export abstract class FieldParser { // Set read only option controlModel.readOnly = this.parserOptions.readOnly; controlModel.disabled = this.parserOptions.readOnly; - controlModel.workspaceItem = this.workspaceItem; controlModel.relationship = this.configData.selectableRelationship; controlModel.repeatable = this.configData.repeatable; controlModel.metadataFields = isNotEmpty(this.configData.selectableMetadata) ? this.configData.selectableMetadata.map((metadataObject) => metadataObject.metadata) : []; diff --git a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts index a72f112212..fab5ec3888 100644 --- a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts @@ -4,12 +4,12 @@ import { ListFieldParser } from './list-field-parser'; import { DynamicListCheckboxGroupModel } from '../ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model'; import { DynamicListRadioGroupModel } from '../ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('ListFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -38,13 +38,13 @@ describe('ListFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new ListFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof ListFieldParser).toBe(true); }); it('should return a DynamicListCheckboxGroupModel object when repeatable option is true', () => { - const parser = new ListFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -53,7 +53,7 @@ describe('ListFieldParser test suite', () => { it('should return a DynamicListRadioGroupModel object when repeatable option is false', () => { field.repeatable = false; - const parser = new ListFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -66,7 +66,7 @@ describe('ListFieldParser test suite', () => { }; const expectedValue = [new FormFieldMetadataValueObject('test type')]; - const parser = new ListFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts index ad3bf08005..5e14e0c013 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts @@ -3,12 +3,12 @@ import { FormFieldMetadataValueObject } from '../models/form-field-metadata-valu import { LookupFieldParser } from './lookup-field-parser'; import { DynamicLookupModel } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('LookupFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -37,13 +37,13 @@ describe('LookupFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new LookupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof LookupFieldParser).toBe(true); }); it('should return a DynamicLookupModel object when repeatable option is false', () => { - const parser = new LookupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -56,7 +56,7 @@ describe('LookupFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test journal'); - const parser = new LookupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts index 574f61b9d0..adc1e90166 100644 --- a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts @@ -1,16 +1,14 @@ import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; -import { LookupFieldParser } from './lookup-field-parser'; -import { DynamicLookupModel } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup.model'; import { LookupNameFieldParser } from './lookup-name-field-parser'; import { DynamicLookupNameModel } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup-name.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('LookupNameFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -39,13 +37,13 @@ describe('LookupNameFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new LookupNameFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof LookupNameFieldParser).toBe(true); }); it('should return a DynamicLookupNameModel object when repeatable option is false', () => { - const parser = new LookupNameFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -58,7 +56,7 @@ describe('LookupNameFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test author'); - const parser = new LookupNameFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts index 116cd76911..1b0c637030 100644 --- a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts @@ -3,7 +3,6 @@ import { NameFieldParser } from './name-field-parser'; import { DynamicConcatModel } from '../ds-dynamic-form-ui/models/ds-dynamic-concat.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('NameFieldParser test suite', () => { let field1: FormFieldModel; @@ -11,6 +10,7 @@ describe('NameFieldParser test suite', () => { let field3: FormFieldModel; let initFormValues: any = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -70,13 +70,13 @@ describe('NameFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new NameFieldParser(field1, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new NameFieldParser(submissionId, field1, initFormValues, parserOptions); expect(parser instanceof NameFieldParser).toBe(true); }); it('should return a DynamicConcatModel object when repeatable option is false', () => { - const parser = new NameFieldParser(field2, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new NameFieldParser(submissionId, field2, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -84,7 +84,7 @@ describe('NameFieldParser test suite', () => { }); it('should return a DynamicConcatModel object with the correct separator', () => { - const parser = new NameFieldParser(field2, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new NameFieldParser(submissionId, field2, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -97,7 +97,7 @@ describe('NameFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test, name'); - const parser = new NameFieldParser(field1, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new NameFieldParser(submissionId, field1, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/name-field-parser.ts b/src/app/shared/form/builder/parsers/name-field-parser.ts index 8e2d3aa992..e5ecb034ea 100644 --- a/src/app/shared/form/builder/parsers/name-field-parser.ts +++ b/src/app/shared/form/builder/parsers/name-field-parser.ts @@ -1,11 +1,17 @@ +import { Inject } from '@angular/core'; import { FormFieldModel } from '../models/form-field.model'; import { ConcatFieldParser } from './concat-field-parser'; +import { CONFIG_DATA, INIT_FORM_VALUES, PARSER_OPTIONS, SUBMISSION_ID } from './field-parser'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; export class NameFieldParser extends ConcatFieldParser { - constructor(protected configData: FormFieldModel, protected initFormValues, protected parserOptions: ParserOptions, wsi: WorkspaceItem) { - super(configData, initFormValues, parserOptions, ',', wsi, 'form.last-name', 'form.first-name'); + constructor( + @Inject(SUBMISSION_ID) submissionId: string, + @Inject(CONFIG_DATA) configData: FormFieldModel, + @Inject(INIT_FORM_VALUES) initFormValues, + @Inject(PARSER_OPTIONS) parserOptions: ParserOptions + ) { + super(submissionId, configData, initFormValues, parserOptions, ',', 'form.last-name', 'form.first-name'); } } diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts index 9c4b3c47f1..4668b3017d 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts @@ -4,13 +4,13 @@ import { DynamicQualdropModel } from '../ds-dynamic-form-ui/models/ds-dynamic-qu import { DynamicTypeaheadModel } from '../ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.model'; import { DsDynamicInputModel } from '../ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('OneboxFieldParser test suite', () => { let field1: FormFieldModel; let field2: FormFieldModel; let field3: FormFieldModel; + const submissionId = '1234'; const initFormValues = {}; const parserOptions: ParserOptions = { readOnly: false, @@ -71,13 +71,13 @@ describe('OneboxFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new OneboxFieldParser(field1, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new OneboxFieldParser(submissionId, field1, initFormValues, parserOptions); expect(parser instanceof OneboxFieldParser).toBe(true); }); it('should return a DynamicQualdropModel object when selectableMetadata is multiple', () => { - const parser = new OneboxFieldParser(field2, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new OneboxFieldParser(submissionId, field2, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -85,7 +85,7 @@ describe('OneboxFieldParser test suite', () => { }); it('should return a DsDynamicInputModel object when selectableMetadata is not multiple', () => { - const parser = new OneboxFieldParser(field3, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new OneboxFieldParser(submissionId, field3, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -93,7 +93,7 @@ describe('OneboxFieldParser test suite', () => { }); it('should return a DynamicTypeaheadModel object when selectableMetadata has authority', () => { - const parser = new OneboxFieldParser(field1, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new OneboxFieldParser(submissionId, field1, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/parser-factory.ts b/src/app/shared/form/builder/parsers/parser-factory.ts index f0fed02466..92d4f01555 100644 --- a/src/app/shared/form/builder/parsers/parser-factory.ts +++ b/src/app/shared/form/builder/parsers/parser-factory.ts @@ -1,6 +1,12 @@ +import { StaticProvider } from '@angular/core'; import { ParserType } from './parser-type'; -import { GenericConstructor } from '../../../../core/shared/generic-constructor'; -import { FieldParser } from './field-parser'; +import { + CONFIG_DATA, + FieldParser, + INIT_FORM_VALUES, + PARSER_OPTIONS, + SUBMISSION_ID +} from './field-parser'; import { DateFieldParser } from './date-field-parser'; import { DropdownFieldParser } from './dropdown-field-parser'; import { RelationGroupFieldParser } from './relation-group-field-parser'; @@ -14,41 +20,92 @@ import { TagFieldParser } from './tag-field-parser'; import { TextareaFieldParser } from './textarea-field-parser'; import { DisabledFieldParser } from './disabled-field-parser'; +const fieldParserDeps = [ + SUBMISSION_ID, + CONFIG_DATA, + INIT_FORM_VALUES, + PARSER_OPTIONS, +]; + export class ParserFactory { - public static getConstructor(type: ParserType): GenericConstructor { + public static getProvider(type: ParserType): StaticProvider { switch (type) { case ParserType.Date: { - return DateFieldParser + return { + provide: FieldParser, + useClass: DateFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Dropdown: { - return DropdownFieldParser + return { + provide: FieldParser, + useClass: DropdownFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.RelationGroup: { - return RelationGroupFieldParser + return { + provide: FieldParser, + useClass: RelationGroupFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.List: { - return ListFieldParser + return { + provide: FieldParser, + useClass: ListFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Lookup: { - return LookupFieldParser + return { + provide: FieldParser, + useClass: LookupFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.LookupName: { - return LookupNameFieldParser + return { + provide: FieldParser, + useClass: LookupNameFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Onebox: { - return OneboxFieldParser + return { + provide: FieldParser, + useClass: OneboxFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Name: { - return NameFieldParser + return { + provide: FieldParser, + useClass: NameFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Series: { - return SeriesFieldParser + return { + provide: FieldParser, + useClass: SeriesFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Tag: { - return TagFieldParser + return { + provide: FieldParser, + useClass: TagFieldParser, + deps: [...fieldParserDeps] + } } case ParserType.Textarea: { - return TextareaFieldParser + return { + provide: FieldParser, + useClass: TextareaFieldParser, + deps: [...fieldParserDeps] + } } case undefined: { return DisabledFieldParser diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts index 63489e7809..84f3df0365 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts @@ -3,12 +3,12 @@ import { RelationGroupFieldParser } from './relation-group-field-parser'; import { DynamicRelationGroupModel } from '../ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('RelationGroupFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -72,13 +72,13 @@ describe('RelationGroupFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new RelationGroupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof RelationGroupFieldParser).toBe(true); }); it('should return a DynamicRelationGroupModel object', () => { - const parser = new RelationGroupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -87,7 +87,7 @@ describe('RelationGroupFieldParser test suite', () => { it('should throw when rows configuration is empty', () => { field.rows = null; - const parser = new RelationGroupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); expect(() => parser.parse()) .toThrow(); @@ -98,7 +98,7 @@ describe('RelationGroupFieldParser test suite', () => { author: [new FormFieldMetadataValueObject('test author')], affiliation: [new FormFieldMetadataValueObject('test affiliation')] }; - const parser = new RelationGroupFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); const expectedValue = [{ diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.ts index b3f6e749f3..01699d9e78 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.ts @@ -15,6 +15,7 @@ export class RelationGroupFieldParser extends FieldParser { public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean) { const modelConfiguration: DynamicRelationGroupModelConfig = this.initModel(null, label); + modelConfiguration.submissionId = this.submissionId; modelConfiguration.scopeUUID = this.parserOptions.authorityUuid; modelConfiguration.submissionScope = this.parserOptions.submissionScope; if (this.configData && this.configData.rows && this.configData.rows.length > 0) { diff --git a/src/app/shared/form/builder/parsers/row-parser.spec.ts b/src/app/shared/form/builder/parsers/row-parser.spec.ts index ab762bc9b2..435c6a6426 100644 --- a/src/app/shared/form/builder/parsers/row-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/row-parser.spec.ts @@ -3,7 +3,6 @@ import { RowParser } from './row-parser'; import { DynamicRowGroupModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { DynamicRowArrayModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { FormRowModel } from '../../../../core/config/models/config-submission-form.model'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('RowParser test suite', () => { @@ -18,6 +17,7 @@ describe('RowParser test suite', () => { let row9: FormRowModel; let row10: FormRowModel; + const submissionId = '1234'; const scopeUUID = 'testScopeUUID'; const initFormValues = {}; const submissionScope = 'WORKSPACE'; @@ -329,76 +329,98 @@ describe('RowParser test suite', () => { }); it('should init parser properly', () => { - let parser = new RowParser(row1, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row2, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row3, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row4, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row5, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row6, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row7, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row8, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row9, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); - - expect(parser instanceof RowParser).toBe(true); - - parser = new RowParser(row10, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); + const parser = new RowParser(undefined); expect(parser instanceof RowParser).toBe(true); }); - it('should return a DynamicRowGroupModel object', () => { - const parser = new RowParser(row1, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); + describe('parse', () => { + it('should return a DynamicRowGroupModel object', () => { + const parser = new RowParser(undefined); - const rowModel = parser.parse(); + const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly); - expect(rowModel instanceof DynamicRowGroupModel).toBe(true); - }); + expect(rowModel instanceof DynamicRowGroupModel).toBe(true); + }); - it('should return a row with three fields', () => { - const parser = new RowParser(row1, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); + it('should return a row with three fields', () => { + const parser = new RowParser(undefined); - const rowModel = parser.parse(); + const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly); - expect((rowModel as DynamicRowGroupModel).group.length).toBe(3); - }); + expect((rowModel as DynamicRowGroupModel).group.length).toBe(3); + }); - it('should return a DynamicRowArrayModel object', () => { - const parser = new RowParser(row2, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); + it('should return a DynamicRowArrayModel object', () => { + const parser = new RowParser(undefined); - const rowModel = parser.parse(); + const rowModel = parser.parse(submissionId, row2, scopeUUID, initFormValues, submissionScope, readOnly); - expect(rowModel instanceof DynamicRowArrayModel).toBe(true); - }); + expect(rowModel instanceof DynamicRowArrayModel).toBe(true); + }); - it('should return a row that contains only scoped fields', () => { - const parser = new RowParser(row3, scopeUUID, initFormValues, new WorkspaceItem(), submissionScope, readOnly); + it('should return a row that contains only scoped fields', () => { + const parser = new RowParser(undefined); - const rowModel = parser.parse(); + const rowModel = parser.parse(submissionId, row3, scopeUUID, initFormValues, submissionScope, readOnly); - expect((rowModel as DynamicRowGroupModel).group.length).toBe(1); + expect((rowModel as DynamicRowGroupModel).group.length).toBe(1); + }); + + it('should be able to parse a dropdown combo field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row4, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); + + it('should be able to parse a lookup-name field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row5, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); + + it('should be able to parse a list field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row6, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); + + it('should be able to parse a date field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row7, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); + + it('should be able to parse a tag field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row8, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); + + it('should be able to parse a textarea field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row9, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); + + it('should be able to parse a group field', () => { + const parser = new RowParser(undefined); + + const rowModel = parser.parse(submissionId, row10, scopeUUID, initFormValues, submissionScope, readOnly); + + expect(rowModel).toBeDefined(); + }); }); }); diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts index 7291b62322..72737cfaa9 100644 --- a/src/app/shared/form/builder/parsers/row-parser.ts +++ b/src/app/shared/form/builder/parsers/row-parser.ts @@ -1,32 +1,42 @@ -import { DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core'; +import { Injectable, Injector } from '@angular/core'; +import { + DYNAMIC_FORM_CONTROL_TYPE_ARRAY, + DynamicFormGroupModelConfig +} from '@ng-dynamic-forms/core'; import { uniqueId } from 'lodash'; import { IntegrationSearchOptions } from '../../../../core/integration/models/integration-options.model'; -import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from '../ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; -import { DynamicRowGroupModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { isEmpty } from '../../../empty.util'; -import { setLayout } from './parser.utils'; +import { DynamicRowGroupModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; +import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from '../ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { FormFieldModel } from '../models/form-field.model'; -import { ParserType } from './parser-type'; -import { ParserOptions } from './parser-options'; +import { + CONFIG_DATA, + FieldParser, + INIT_FORM_VALUES, + PARSER_OPTIONS, + SUBMISSION_ID +} from './field-parser'; import { ParserFactory } from './parser-factory'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; +import { ParserOptions } from './parser-options'; +import { ParserType } from './parser-type'; +import { setLayout } from './parser.utils'; export const ROW_ID_PREFIX = 'df-row-group-config-'; +@Injectable({ + providedIn: 'root' +}) export class RowParser { - protected authorityOptions: IntegrationSearchOptions; - - constructor(protected rowData, - protected scopeUUID, - protected initFormValues: any, - protected wsi: WorkspaceItem, - protected submissionScope, - protected readOnly: boolean) { - this.authorityOptions = new IntegrationSearchOptions(scopeUUID); + constructor(private parentInjector: Injector) { } - public parse(): DynamicRowGroupModel { + public parse(submissionId: string, + rowData, + scopeUUID, + initFormValues: any, + submissionScope, + readOnly: boolean): DynamicRowGroupModel { let fieldModel: any = null; let parsedResult = null; const config: DynamicFormGroupModelConfig = { @@ -34,31 +44,44 @@ export class RowParser { group: [], }; - const scopedFields: FormFieldModel[] = this.filterScopedFields(this.rowData.fields); + const authorityOptions = new IntegrationSearchOptions(scopeUUID); + + const scopedFields: FormFieldModel[] = this.filterScopedFields(rowData.fields, submissionScope); const layoutDefaultGridClass = ' col-sm-' + Math.trunc(12 / scopedFields.length); const layoutClass = ' d-flex flex-column justify-content-start'; const parserOptions: ParserOptions = { - readOnly: this.readOnly, - submissionScope: this.submissionScope, - authorityUuid: this.authorityOptions.uuid + readOnly: readOnly, + submissionScope: submissionScope, + authorityUuid: authorityOptions.uuid }; // Iterate over row's fields scopedFields.forEach((fieldData: FormFieldModel) => { const layoutFieldClass = (fieldData.style || layoutDefaultGridClass) + layoutClass; - const parserCo = ParserFactory.getConstructor(fieldData.input.type as ParserType); - if (parserCo) { - fieldModel = new parserCo(fieldData, this.initFormValues, parserOptions, this.wsi).parse(); + const parserProvider = ParserFactory.getProvider(fieldData.input.type as ParserType); + if (parserProvider) { + const fieldInjector = Injector.create({ + providers: [ + parserProvider, + { provide: SUBMISSION_ID, useValue: submissionId }, + { provide: CONFIG_DATA, useValue: fieldData }, + { provide: INIT_FORM_VALUES, useValue: initFormValues }, + { provide: PARSER_OPTIONS, useValue: parserOptions } + ], + parent: this.parentInjector + }); + + fieldModel = fieldInjector.get(FieldParser).parse(); } else { - throw new Error(`unknown form control model type "${fieldData.input.type}" defined for Input field with label "${fieldData.label}".`, ); + throw new Error(`unknown form control model type "${fieldData.input.type}" defined for Input field with label "${fieldData.label}".`,); } if (fieldModel) { if (fieldModel.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY || fieldModel.type === DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP) { - if (this.rowData.fields.length > 1) { + if (rowData.fields.length > 1) { setLayout(fieldModel, 'grid', 'host', layoutFieldClass); config.group.push(fieldModel); // if (isEmpty(parsedResult)) { @@ -100,15 +123,15 @@ export class RowParser { return parsedResult; } - checksFieldScope(fieldScope) { - return (isEmpty(fieldScope) || isEmpty(this.submissionScope) || fieldScope === this.submissionScope); + checksFieldScope(fieldScope, submissionScope) { + return (isEmpty(fieldScope) || isEmpty(submissionScope) || fieldScope === submissionScope); } - filterScopedFields(fields: FormFieldModel[]): FormFieldModel[] { + filterScopedFields(fields: FormFieldModel[], submissionScope): FormFieldModel[] { const filteredFields: FormFieldModel[] = []; fields.forEach((field: FormFieldModel) => { // Whether field scope doesn't match the submission scope, skip it - if (this.checksFieldScope(field.scope)) { + if (this.checksFieldScope(field.scope, submissionScope)) { filteredFields.push(field); } }); diff --git a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts index 7b0f8a3bb3..ceb4e96320 100644 --- a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts @@ -3,12 +3,12 @@ import { DynamicConcatModel } from '../ds-dynamic-form-ui/models/ds-dynamic-conc import { SeriesFieldParser } from './series-field-parser'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('SeriesFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -33,13 +33,13 @@ describe('SeriesFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new SeriesFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof SeriesFieldParser).toBe(true); }); it('should return a DynamicConcatModel object when repeatable option is false', () => { - const parser = new SeriesFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -47,7 +47,7 @@ describe('SeriesFieldParser test suite', () => { }); it('should return a DynamicConcatModel object with the correct separator', () => { - const parser = new SeriesFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -60,7 +60,7 @@ describe('SeriesFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test; series'); - const parser = new SeriesFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/series-field-parser.ts b/src/app/shared/form/builder/parsers/series-field-parser.ts index 37db1cd15c..36ee9c36c1 100644 --- a/src/app/shared/form/builder/parsers/series-field-parser.ts +++ b/src/app/shared/form/builder/parsers/series-field-parser.ts @@ -1,11 +1,17 @@ +import { Inject } from '@angular/core'; import { FormFieldModel } from '../models/form-field.model'; import { ConcatFieldParser } from './concat-field-parser'; +import { CONFIG_DATA, INIT_FORM_VALUES, PARSER_OPTIONS, SUBMISSION_ID } from './field-parser'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; export class SeriesFieldParser extends ConcatFieldParser { - constructor(protected configData: FormFieldModel, protected initFormValues, protected parserOptions: ParserOptions, wsi: WorkspaceItem) { - super(configData, initFormValues, parserOptions, ';', wsi); + constructor( + @Inject(SUBMISSION_ID) submissionId: string, + @Inject(CONFIG_DATA) configData: FormFieldModel, + @Inject(INIT_FORM_VALUES) initFormValues, + @Inject(PARSER_OPTIONS) parserOptions: ParserOptions + ) { + super(submissionId, configData, initFormValues, parserOptions, ';'); } } diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts index 7466dd456f..90449e62e5 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts @@ -3,12 +3,12 @@ import { FormFieldMetadataValueObject } from '../models/form-field-metadata-valu import { TagFieldParser } from './tag-field-parser'; import { DynamicTagModel } from '../ds-dynamic-form-ui/models/tag/dynamic-tag.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('TagFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', @@ -37,13 +37,13 @@ describe('TagFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new TagFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof TagFieldParser).toBe(true); }); it('should return a DynamicTagModel object when repeatable option is false', () => { - const parser = new TagFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -58,7 +58,7 @@ describe('TagFieldParser test suite', () => { ], }; - const parser = new TagFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts index 056ab29b6a..167f126cf2 100644 --- a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts @@ -3,12 +3,12 @@ import { FormFieldMetadataValueObject } from '../models/form-field-metadata-valu import { TextareaFieldParser } from './textarea-field-parser'; import { DsDynamicTextAreaModel } from '../ds-dynamic-form-ui/models/ds-dynamic-textarea.model'; import { ParserOptions } from './parser-options'; -import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; describe('TextareaFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + const submissionId = '1234'; const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, @@ -35,13 +35,13 @@ describe('TextareaFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new TextareaFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions); expect(parser instanceof TextareaFieldParser).toBe(true); }); it('should return a DsDynamicTextAreaModel object when repeatable option is false', () => { - const parser = new TextareaFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); @@ -56,7 +56,7 @@ describe('TextareaFieldParser test suite', () => { }; const expectedValue ='test description'; - const parser = new TextareaFieldParser(field, initFormValues, parserOptions, new WorkspaceItem()); + const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions); const fieldModel = parser.parse(); diff --git a/src/app/shared/mocks/mock-form-models.ts b/src/app/shared/mocks/mock-form-models.ts index 96647d654e..ae5a97ca65 100644 --- a/src/app/shared/mocks/mock-form-models.ts +++ b/src/app/shared/mocks/mock-form-models.ts @@ -118,6 +118,7 @@ const mockFormRowModel = { } as FormRowModel; const relationGroupConfig = { + submissionId: '1234', id: 'relationGroup', formConfiguration: [mockFormRowModel], mandatoryField: 'false', diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index e21291dabd..a28a35c99d 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -30,6 +30,7 @@ import { AbstractListableElementComponent } from './object-collection/shared/obj import { ObjectGridComponent } from './object-grid/object-grid.component'; import { ObjectCollectionComponent } from './object-collection/object-collection.component'; import { ComcolPageContentComponent } from './comcol-page-content/comcol-page-content.component'; +import { ComcolPageHandleComponent } from './comcol-page-handle/comcol-page-handle.component'; import { ComcolPageHeaderComponent } from './comcol-page-header/comcol-page-header.component'; import { ComcolPageLogoComponent } from './comcol-page-logo/comcol-page-logo.component'; import { ErrorComponent } from './error/error.component'; @@ -215,6 +216,7 @@ const COMPONENTS = [ UserMenuComponent, ChipsComponent, ComcolPageContentComponent, + ComcolPageHandleComponent, ComcolPageHeaderComponent, ComcolPageLogoComponent, ComColFormComponent, diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts index b592972839..1732075bf8 100644 --- a/src/app/submission/form/submission-form.component.ts +++ b/src/app/submission/form/submission-form.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; import { of as observableOf, Observable, Subscription } from 'rxjs'; -import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; +import { distinctUntilChanged, filter, flatMap, map, switchMap } from 'rxjs/operators'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { SubmissionObjectEntry } from '../objects/submission-objects.reducer'; @@ -125,7 +125,7 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { map((submission: SubmissionObjectEntry) => submission.isLoading), map((isLoading: boolean) => isLoading), distinctUntilChanged(), - flatMap((isLoading: boolean) => { + switchMap((isLoading: boolean) => { if (!isLoading) { return this.getSectionsList(); } else { diff --git a/src/app/submission/sections/form/section-form.component.ts b/src/app/submission/sections/form/section-form.component.ts index f9b3ff78b0..dcf1aac29d 100644 --- a/src/app/submission/sections/form/section-form.component.ts +++ b/src/app/submission/sections/form/section-form.component.ts @@ -229,6 +229,7 @@ export class SubmissionSectionformComponent extends SectionModelComponent { initForm(sectionData: WorkspaceitemSectionFormObject): void { try { this.formModel = this.formBuilderService.modelFromConfiguration( + this.submissionId, this.formConfig, this.collectionId, sectionData, diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 8040b7d818..cde004f4b6 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -166,6 +166,7 @@ export class SubmissionSectionUploadFileEditComponent implements OnChanges { const formModel: DynamicFormControlModel[] = []; const metadataGroupModelConfig = Object.assign({}, BITSTREAM_METADATA_FORM_GROUP_CONFIG); metadataGroupModelConfig.group = this.formBuilderService.modelFromConfiguration( + this.submissionId, configForm, this.collectionId, this.fileData.metadata,