From 31fd89a9fce0ae506477e160d0bb86be86146c2f Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 9 Dec 2021 18:22:35 +0100 Subject: [PATCH 01/19] [CST-4878] Created component --- .../accesses/section-accesses.component.html | 1 + .../accesses/section-accesses.component.scss | 0 .../section-accesses.component.spec.ts | 25 +++++++++++++++++++ .../accesses/section-accesses.component.ts | 20 +++++++++++++++ src/app/submission/sections/sections-type.ts | 3 ++- 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/app/submission/sections/accesses/section-accesses.component.html create mode 100644 src/app/submission/sections/accesses/section-accesses.component.scss create mode 100644 src/app/submission/sections/accesses/section-accesses.component.spec.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.component.ts diff --git a/src/app/submission/sections/accesses/section-accesses.component.html b/src/app/submission/sections/accesses/section-accesses.component.html new file mode 100644 index 00000000000..b7acab843b4 --- /dev/null +++ b/src/app/submission/sections/accesses/section-accesses.component.html @@ -0,0 +1 @@ +

section-accesses works!

diff --git a/src/app/submission/sections/accesses/section-accesses.component.scss b/src/app/submission/sections/accesses/section-accesses.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts new file mode 100644 index 00000000000..cc8f1f1ab4e --- /dev/null +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubmissionSectionAccessesComponent } from './section-accesses.component'; + +describe('SubmissionSectionAccessesComponent', () => { + let component: SubmissionSectionAccessesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SubmissionSectionAccessesComponent] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SubmissionSectionAccessesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/submission/sections/accesses/section-accesses.component.ts b/src/app/submission/sections/accesses/section-accesses.component.ts new file mode 100644 index 00000000000..2d757f5e0c4 --- /dev/null +++ b/src/app/submission/sections/accesses/section-accesses.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { renderSectionFor } from 'src/app/submission/sections/sections-decorator'; +import { SectionsType } from 'src/app/submission/sections/sections-type'; + +@Component({ + selector: 'ds-section-accesses', + templateUrl: './section-accesses.component.html', + styleUrls: ['./section-accesses.component.scss'] +}) +@renderSectionFor(SectionsType.Accesses) +export class SubmissionSectionAccessesComponent implements OnInit { + + // tslint:disable-next-line:no-empty + constructor() { } + + // tslint:disable-next-line:no-empty + ngOnInit(): void { + } + +} diff --git a/src/app/submission/sections/sections-type.ts b/src/app/submission/sections/sections-type.ts index 34ecafe42be..37b7cc5e745 100644 --- a/src/app/submission/sections/sections-type.ts +++ b/src/app/submission/sections/sections-type.ts @@ -4,5 +4,6 @@ export enum SectionsType { Upload = 'upload', License = 'license', CcLicense = 'cclicense', - collection = 'collection' + collection = 'collection', + Accesses = 'accesses', } From f04f4b4f346dbab6b218da82bc7cea27adc982c3 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Tue, 21 Dec 2021 13:35:29 +0100 Subject: [PATCH 02/19] [CST-4878] Finished working on embargo add part of form and unit testing --- ...onfig-accesses-conditions-options.model.ts | 48 +++ .../models/config-submission-access.model.ts | 27 ++ .../config-submission-accesses.model.ts | 10 + src/app/core/config/models/config-type.ts | 2 + .../submission-accesses-config.service.ts | 44 ++ src/app/core/core.module.ts | 4 +- .../models/submission-accesses.model.ts | 37 ++ .../submission-accesses.resource-type.ts | 9 + .../workspaceitem-section-accesses.model.ts | 18 + .../models/workspaceitem-sections.model.ts | 2 + .../section-accesses-config.service.mock.ts | 44 ++ .../mocks/section-accesses.service.mock.ts | 13 + src/app/shared/mocks/submission.mock.ts | 93 +++- src/app/shared/testing/form-event.stub.ts | 106 +++++ .../edit/submission-edit.component.ts | 17 +- .../form/submission-form.component.ts | 11 +- .../objects/submission-objects.effects.ts | 46 +- .../accesses/section-accesses.component.html | 3 +- .../section-accesses.component.spec.ts | 127 +++++- .../accesses/section-accesses.component.ts | 398 +++++++++++++++++- .../accesses/section-accesses.model.ts | 117 +++++ .../accesses/section-accesses.service.ts | 44 ++ .../section-container.component.html | 48 +-- .../container/section-container.component.ts | 6 +- .../sections/models/section.model.ts | 4 +- src/app/submission/sections/sections-type.ts | 2 +- .../file/section-upload-file.component.ts | 16 +- src/app/submission/selectors.ts | 11 +- src/app/submission/submission.module.ts | 10 +- src/assets/i18n/en.json5 | 3 +- 30 files changed, 1219 insertions(+), 101 deletions(-) create mode 100644 src/app/core/config/models/config-accesses-conditions-options.model.ts create mode 100644 src/app/core/config/models/config-submission-access.model.ts create mode 100644 src/app/core/config/models/config-submission-accesses.model.ts create mode 100644 src/app/core/config/submission-accesses-config.service.ts create mode 100644 src/app/core/submission/models/submission-accesses.model.ts create mode 100644 src/app/core/submission/models/submission-accesses.resource-type.ts create mode 100644 src/app/core/submission/models/workspaceitem-section-accesses.model.ts create mode 100644 src/app/shared/mocks/section-accesses-config.service.mock.ts create mode 100644 src/app/shared/mocks/section-accesses.service.mock.ts create mode 100644 src/app/shared/testing/form-event.stub.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.model.ts create mode 100644 src/app/submission/sections/accesses/section-accesses.service.ts diff --git a/src/app/core/config/models/config-accesses-conditions-options.model.ts b/src/app/core/config/models/config-accesses-conditions-options.model.ts new file mode 100644 index 00000000000..a12ae76b8e7 --- /dev/null +++ b/src/app/core/config/models/config-accesses-conditions-options.model.ts @@ -0,0 +1,48 @@ +import { autoserialize } from 'cerialize'; + +/** + * Model class for an Access Condition + */ +export class AccessesConditionOption { + + /** + * The name for this Access Condition + */ + name: string; + + /** + * The groupName for this Access Condition + */ + groupName: string; + + + /** + * A boolean representing if this Access Condition has a start date + */ + hasStartDate: boolean; + + /** + * A boolean representing if this Access Condition has an end date + */ + hasEndDate: boolean; + + /** + * Maximum value of the start date + */ + endDateLimit?: string; + + /** + * Maximum value of the end date + */ + startDateLimit?: string; + + /** + * Maximum value of the start date + */ + maxStartDate?: string; + + /** + * Maximum value of the end date + */ + maxEndDate?: string; +} diff --git a/src/app/core/config/models/config-submission-access.model.ts b/src/app/core/config/models/config-submission-access.model.ts new file mode 100644 index 00000000000..a13a42a303f --- /dev/null +++ b/src/app/core/config/models/config-submission-access.model.ts @@ -0,0 +1,27 @@ +import { autoserialize, inheritSerialization, deserialize } from 'cerialize'; +import { typedObject, link } from '../../cache/builders/build-decorators'; +import { ConfigObject } from './config.model'; +import { AccessesConditionOption } from './config-accesses-conditions-options.model'; +import { SUBMISSION_ACCESSES_TYPE } from './config-type'; +import { HALLink } from '../../shared/hal-link.model'; + + +@typedObject +@inheritSerialization(ConfigObject) +export class SubmissionAccessModel extends ConfigObject { + static type = SUBMISSION_ACCESSES_TYPE; + /** + * A list of available bitstream access conditions + */ + @autoserialize + accessConditionOptions: AccessesConditionOption[]; + + @autoserialize + discoverable: boolean; + + @deserialize + _links: { + self: HALLink + }; + +} diff --git a/src/app/core/config/models/config-submission-accesses.model.ts b/src/app/core/config/models/config-submission-accesses.model.ts new file mode 100644 index 00000000000..3f8004928db --- /dev/null +++ b/src/app/core/config/models/config-submission-accesses.model.ts @@ -0,0 +1,10 @@ +import { inheritSerialization } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; +import { SUBMISSION_ACCESSES_TYPE } from './config-type'; +import { SubmissionAccessModel } from './config-submission-access.model'; + +@typedObject +@inheritSerialization(SubmissionAccessModel) +export class SubmissionAccessesModel extends SubmissionAccessModel { + static type = SUBMISSION_ACCESSES_TYPE; +} diff --git a/src/app/core/config/models/config-type.ts b/src/app/core/config/models/config-type.ts index 858ff19c91d..19864536f06 100644 --- a/src/app/core/config/models/config-type.ts +++ b/src/app/core/config/models/config-type.ts @@ -15,3 +15,5 @@ export const SUBMISSION_SECTION_TYPE = new ResourceType('submissionsection'); export const SUBMISSION_UPLOADS_TYPE = new ResourceType('submissionuploads'); export const SUBMISSION_UPLOAD_TYPE = new ResourceType('submissionupload'); + +export const SUBMISSION_ACCESSES_TYPE = new ResourceType('submissionaccessoption'); diff --git a/src/app/core/config/submission-accesses-config.service.ts b/src/app/core/config/submission-accesses-config.service.ts new file mode 100644 index 00000000000..ace3fd2316e --- /dev/null +++ b/src/app/core/config/submission-accesses-config.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { ConfigService } from './config.service'; +import { RequestService } from '../data/request.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { dataService } from '../cache/builders/build-decorators'; +import { SUBMISSION_ACCESSES_TYPE } from './models/config-type'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; +import { ConfigObject } from './models/config.model'; +import { SubmissionAccessesModel } from './models/config-submission-accesses.model'; +import { RemoteData } from '../data/remote-data'; +import { Observable } from 'rxjs'; +import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { map, switchMap } from 'rxjs/operators'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; + +/** + * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. + */ +@Injectable() +@dataService(SUBMISSION_ACCESSES_TYPE) +export class SubmissionAccessesConfigService extends ConfigService { + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer + ) { + super(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, 'submissionaccessoptions'); + } + + findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable> { + return super.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as FollowLinkConfig[]) as Observable>; + } +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 026c87be9d1..09f8616f9ec 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -163,6 +163,7 @@ import { RootDataService } from './data/root-data.service'; import { Root } from './data/root.model'; import { SearchConfig } from './shared/search/search-filters/search-config.model'; import { SequenceService } from './shared/sequence.service'; +import { SubmissionAccessesModel } from 'src/app/core/config/models/config-submission-accesses.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -343,7 +344,8 @@ export const models = Registration, UsageReport, Root, - SearchConfig + SearchConfig, + SubmissionAccessesModel ]; @NgModule({ diff --git a/src/app/core/submission/models/submission-accesses.model.ts b/src/app/core/submission/models/submission-accesses.model.ts new file mode 100644 index 00000000000..2affe641f5f --- /dev/null +++ b/src/app/core/submission/models/submission-accesses.model.ts @@ -0,0 +1,37 @@ +import { autoserialize, inheritSerialization } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; +import { excludeFromEquals } from '../../utilities/equals.decorators'; +import { ResourceType } from '../../shared/resource-type'; +import { HALResource } from '../../shared/hal-resource.model'; +import { SUBMISSION_ACCESSES } from 'src/app/core/submission/models/submission-accesses.resource-type'; + +@typedObject +@inheritSerialization(HALResource) +export class SubmissionAccesses extends HALResource { + + static type = SUBMISSION_ACCESSES; + + /** + * The object type + */ + @excludeFromEquals + @autoserialize + type: ResourceType; + + @autoserialize + discoverable: boolean; + + @autoserialize + accessConditions: AccessConditions[]; +} + +export interface AccessConditions { + name: string; + startDate?: Date; + hasStartDate?: boolean; + maxStartDate?: string; + hasEndDate?: boolean; + maxEndDate?: string; + endDate?: Date; +} + diff --git a/src/app/core/submission/models/submission-accesses.resource-type.ts b/src/app/core/submission/models/submission-accesses.resource-type.ts new file mode 100644 index 00000000000..0aacb895223 --- /dev/null +++ b/src/app/core/submission/models/submission-accesses.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from '../../shared/resource-type'; + +/** + * The resource type for License + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SUBMISSION_ACCESSES = new ResourceType('submissionaccesses'); diff --git a/src/app/core/submission/models/workspaceitem-section-accesses.model.ts b/src/app/core/submission/models/workspaceitem-section-accesses.model.ts new file mode 100644 index 00000000000..c464f858cfa --- /dev/null +++ b/src/app/core/submission/models/workspaceitem-section-accesses.model.ts @@ -0,0 +1,18 @@ +/** + * An interface to represent the submission's creative commons license section data. + */ +export interface WorkspaceitemSectionAccessesObject { + id: string; + discoverable: boolean; + accessConditions: [ + { + name: string; + startDate?: Date; + hasStartDate?: boolean; + maxStartDate?: string; + hasEndDate?: boolean; + maxEndDate?: string; + endDate?: Date; + } + ]; +} diff --git a/src/app/core/submission/models/workspaceitem-sections.model.ts b/src/app/core/submission/models/workspaceitem-sections.model.ts index 6ff756a3236..084da3f0888 100644 --- a/src/app/core/submission/models/workspaceitem-sections.model.ts +++ b/src/app/core/submission/models/workspaceitem-sections.model.ts @@ -1,3 +1,4 @@ +import { WorkspaceitemSectionAccessesObject } from './workspaceitem-section-accesses.model'; import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model'; import { WorkspaceitemSectionLicenseObject } from './workspaceitem-section-license.model'; import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload.model'; @@ -19,4 +20,5 @@ export type WorkspaceitemSectionDataType | WorkspaceitemSectionFormObject | WorkspaceitemSectionLicenseObject | WorkspaceitemSectionCcLicenseObject + | WorkspaceitemSectionAccessesObject | string; diff --git a/src/app/shared/mocks/section-accesses-config.service.mock.ts b/src/app/shared/mocks/section-accesses-config.service.mock.ts new file mode 100644 index 00000000000..d26420443ec --- /dev/null +++ b/src/app/shared/mocks/section-accesses-config.service.mock.ts @@ -0,0 +1,44 @@ +import { SubmissionFormsConfigService } from '../../core/config/submission-forms-config.service'; +import { SubmissionFormsModel } from 'src/app/core/config/models/config-submission-forms.model'; +import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; + +const configRes = Object.assign(new SubmissionFormsModel(), { + 'id': 'AccessConditionDefaultConfiguration', + 'canChangeDiscoverable': true, + 'accessConditionOptions': [ + { + 'name': 'openaccess', + 'hasStartDate': false, + 'hasEndDate': false + }, + { + 'name': 'lease', + 'hasStartDate': false, + 'hasEndDate': true, + 'maxEndDate': '2022-06-20T12:17:44.420+00:00' + }, + { + 'name': 'embargo', + 'hasStartDate': true, + 'hasEndDate': false, + 'maxStartDate': '2024-12-20T12:17:44.420+00:00' + }, + { + 'name': 'administrator', + 'hasStartDate': false, + 'hasEndDate': false + } + ], + 'type': 'submissionaccessoption', + '_links': { + 'self': { + 'href': 'http://localhost:8080/server/api/config/submissionaccessoptions/AccessConditionDefaultConfiguration' + } + } +}); + +export function getSubmissionAccessesConfigService(): SubmissionFormsConfigService { + return jasmine.createSpyObj('SubmissionAccessesConfigService', { + findByHref: createSuccessfulRemoteDataObject$(configRes), + }); +} diff --git a/src/app/shared/mocks/section-accesses.service.mock.ts b/src/app/shared/mocks/section-accesses.service.mock.ts new file mode 100644 index 00000000000..5f2f5defc14 --- /dev/null +++ b/src/app/shared/mocks/section-accesses.service.mock.ts @@ -0,0 +1,13 @@ +import { SubmissionFormsModel } from 'src/app/core/config/models/config-submission-forms.model'; +import { of as observableOf } from 'rxjs'; + +const dataRes = Object.assign(new SubmissionFormsModel(), { + 'id': 'AccessConditionDefaultConfiguration', + 'accessConditions': [], +}); + +export function getSectionAccessesService() { + return jasmine.createSpyObj('SectionAccessesService', { + getAccessesData: observableOf(dataRes), + }); +} diff --git a/src/app/shared/mocks/submission.mock.ts b/src/app/shared/mocks/submission.mock.ts index 04461598ced..b90cababcbe 100644 --- a/src/app/shared/mocks/submission.mock.ts +++ b/src/app/shared/mocks/submission.mock.ts @@ -1654,7 +1654,7 @@ export const mockFileFormData = { }, }, { - accessConditionGroup:{ + accessConditionGroup: { name: [ { value: 'lease', @@ -1723,3 +1723,94 @@ export const mockFileFormData = { } ] }; + + +export const mockAccessesFormData = { + discoverable: true, + accessCondition: [ + { + accessConditionGroup: { + name: [ + { + value: 'openaccess', + language: null, + authority: null, + display: 'openaccess', + confidence: -1, + place: 0, + otherInformation: null + } + ], + }, + }, + { + accessConditionGroup: { + name: [ + { + value: 'lease', + language: null, + authority: null, + display: 'lease', + confidence: -1, + place: 0, + otherInformation: null + } + ], + endDate: [ + { + value: { + year: 2019, + month: 1, + day: 16 + }, + language: null, + authority: null, + display: { + year: 2019, + month: 1, + day: 16 + }, + confidence: -1, + place: 0, + otherInformation: null + } + ], + } + }, + { + accessConditionGroup: { + name: [ + { + value: 'embargo', + language: null, + authority: null, + display: 'lease', + confidence: -1, + place: 0, + otherInformation: null + } + ], + startDate: [ + { + value: { + year: 2019, + month: 1, + day: 16 + }, + language: null, + authority: null, + display: { + year: 2019, + month: 1, + day: 16 + }, + confidence: -1, + place: 0, + otherInformation: null + } + ], + } + } + ] +}; + diff --git a/src/app/shared/testing/form-event.stub.ts b/src/app/shared/testing/form-event.stub.ts new file mode 100644 index 00000000000..a2b1f180310 --- /dev/null +++ b/src/app/shared/testing/form-event.stub.ts @@ -0,0 +1,106 @@ +import { FormControl, FormGroup, AbstractControl } from '@angular/forms'; +import { DynamicCheckboxModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; + +export const accessConditionChangeEvent = { + $event: { + bubbles: true, + cancelBubble: false, + cancelable: false, + composed: false, + currentTarget: null, + defaultPrevented: false, + eventPhase: 0, + isTrusted: true, + path: ['input#accessCondition-0-endDate.form-control.ng-touched.ng-dirty.ng-valid', 'div.input-group.ng-touched.ng-valid.ng-pristine', 'ds-dynamic-date-picker-inline.ng-star-inserted, div, div, div, div.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-control-container.col-6.ng-star-inserted, div#accessCondition-0-accessConditionGroup.form-row.ng-star-inserted.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-group.ng-star-inserted, div, div, div, div.pl-1.pr-1.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-control-container.form-group.flex-fill.access-condition-group.ng-star-inserted.ng-to…, div.cdk-drag.cdk-drag-handle.form-row.cdk-drag-disabled.ng-star-inserted.ng-touched.ng-valid.ng-pris…, div#cdk-drop-list-5.cdk-drop-list, div#accessCondition.ng-star-inserted.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-array.ng-star-inserted, div, div, div, div.form-group.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-control-container.ng-star-inserted, ds-dynamic-form.ng-touched.ng-valid.ng-pristine, form.form-horizontal.ng-touched.ng-valid.ng-pristine, div.container-fluid, ds-form.ng-star-inserted, ds-section-accesses.ng-star-inserted, div#sectionContent_AccessConditionDefaultConfiguration.ng-star-inserted, div.card-body, div#AccessConditionDefaultConfiguration.collapse.show.ng-star-inserted, div.card.ng-star-inserted, ngb-accordion.accordion, div#section_AccessConditionDefaultConfiguration.section-focus, ds-submission-section-container.ng-star-inserted, div.submission-form-content, div.container-fluid, ds-submission-form, div.submission-submit-container, ds-submission-edit.ng-star-inserted, ds-themed-submission-edit.ng-star-inserted, div.ng-tns-c392-0, main.main-content.ng-tns-c392-0, div.inner-wrapper.ng-tns-c392-0.ng-trigger.ng-trigger-slideSidebarPadding, div.outer-wrapper.ng-tns-c392-0.ng-star-inserted, ds-root.ng-tns-c392-0.ng-star-inserted, ds-themed-root, ds-app, body, html.wf-droidsans-n4-active.wf-active, document, Window'], + returnValue: true, + srcElement: 'input#accessCondition-0-endDate.form-control.ng-touched.ng-dirty.ng-valid', + target: 'input#accessCondition-0-endDate.form-control.ng-touched.ng-dirty.ng-valid', + timeStamp: 143042.8999999999, + type: 'change', + }, + context: null, + control: new FormControl({ + errors: null, + pristine: false, + status: 'VALID', + statusChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false }, + touched: true, + value: { year: 2021, month: 12, day: 30 }, + valueChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false }, + _updateOn: 'change', + }), + group: new FormGroup({}), + model: new DynamicSelectModel({ + additional: null, + asyncValidators: null, + controlTooltip: null, + errorMessages: { required: 'submission.sections.upload.form.date-required-until' }, + hidden: false, + hint: null, + id: 'endDate', + label: 'submission.sections.upload.form.until-label', + labelTooltip: null, + name: 'endDate', + placeholder: 'Until', + prefix: null, + relations: [], + required: true, + suffix: null, + tabIndex: null, + updateOn: null, + validators: { required: null }, + }), + type: 'change' +}; + + +export const checkboxChangeEvent = { + $event: { + bubbles: true, + cancelBubble: false, + cancelable: false, + composed: false, + currentTarget: null, + defaultPrevented: false, + eventPhase: 0, + isTrusted: true, + path: ['input#discoverable.form-check-input.ng-valid.ng-touched.ng-pristine', 'label.form-check-label', 'div.form-check.ng-touched.ng-valid.ng-pristine', 'dynamic-ng-bootstrap-checkbox.ng-star-inserted', 'div', 'div', 'div', 'div.form-group.ng-touched.ng-valid.ng-pristine', 'ds-dynamic-form-control-container.ng-star-inserted', 'ds-dynamic-form.ng-touched.ng-valid.ng-pristine', 'form.form-horizontal.ng-touched.ng-valid.ng-pristine', 'div.container-fluid', 'ds-form.ng-star-inserted', 'ds-section-accesses.ng-star-inserted', 'div#sectionContent_AccessConditionDefaultConfiguration.ng-star-inserted', 'div.card-body', 'div#AccessConditionDefaultConfiguration.collapse.show.ng-star-inserted', 'div.card.ng-star-inserted', 'ngb-accordion.accordion', 'div#section_AccessConditionDefaultConfiguration.section-focus', 'ds-submission-section-container.ng-star-inserted', 'div.submission-form-content', 'div.container-fluid', 'ds-submission-form', 'div.submission-submit-container', 'ds-submission-edit.ng-star-inserted', 'ds-themed-submission-edit.ng-star-inserted', 'div.ng-tns-c392-0', 'main.main-content.ng-tns-c392-0', 'div.inner-wrapper.ng-tns-c392-0.ng-trigger.ng-trigger-slideSidebarPadding', 'div.outer-wrapper.ng-tns-c392-0.ng-star-inserted', 'ds-root.ng-tns-c392-0.ng-star-inserted', 'ds-themed-root', 'ds-app', 'body', 'html.wf-droidsans-n4-active.wf-active', 'document', 'Window'], + returnValue: true, + srcElement: 'input#discoverable.form-check-input.ng-valid.ng-touched.ng-pristine', + target: 'input#discoverable.form-check-input.ng-valid.ng-touched.ng-pristine', + timeStamp: 143042.8999999999, + type: 'change', + }, + context: null, + control: new FormControl({ + errors: null, + pristine: false, + status: 'VALID', + statusChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false }, + touched: true, + value: { year: 2021, month: 12, day: 30 }, + valueChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false }, + _updateOn: 'change', + }), + group: new FormGroup({}), + model: new DynamicCheckboxModel({ + additional: null, + asyncValidators: null, + controlTooltip: null, + errorMessages: null, + hidden: false, + hint: null, + id: 'discoverable', + indeterminate: false, + label: 'Discoverable', + labelPosition: null, + labelTooltip: null, + name: 'discoverable', + relations: [], + required: false, + tabIndex: null, + updateOn: null, + validators: { required: null }, + }), + type: 'change' +}; diff --git a/src/app/submission/edit/submission-edit.component.ts b/src/app/submission/edit/submission-edit.component.ts index c415b89b81c..008169001d0 100644 --- a/src/app/submission/edit/submission-edit.component.ts +++ b/src/app/submission/edit/submission-edit.component.ts @@ -6,6 +6,7 @@ import { debounceTime, filter, switchMap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model'; +import { WorkspaceitemSectionAccessesObject } from '../../core/submission/models/workspaceitem-section-accesses.model'; import { hasValue, isEmpty, isNotEmptyOperator, isNotNull } from '../../shared/empty.util'; import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model'; import { SubmissionService } from '../submission.service'; @@ -97,13 +98,13 @@ export class SubmissionEditComponent implements OnDestroy, OnInit { * @param {SubmissionJsonPatchOperationsService} submissionJsonPatchOperationsService */ constructor(private changeDetectorRef: ChangeDetectorRef, - private notificationsService: NotificationsService, - private route: ActivatedRoute, - private router: Router, - private itemDataService: ItemDataService, - private submissionService: SubmissionService, - private translate: TranslateService, - private submissionJsonPatchOperationsService: SubmissionJsonPatchOperationsService) { + private notificationsService: NotificationsService, + private route: ActivatedRoute, + private router: Router, + private itemDataService: ItemDataService, + private submissionService: SubmissionService, + private translate: TranslateService, + private submissionJsonPatchOperationsService: SubmissionJsonPatchOperationsService) { } /** @@ -127,6 +128,8 @@ export class SubmissionEditComponent implements OnDestroy, OnInit { this.collectionId = (submissionObjectRD.payload.collection as Collection).id; this.selfUrl = submissionObjectRD.payload._links.self.href; this.sections = submissionObjectRD.payload.sections; + + this.itemLink$.next(submissionObjectRD.payload._links.item.href); this.item = submissionObjectRD.payload.item; this.submissionDefinition = (submissionObjectRD.payload.submissionDefinition as SubmissionDefinitionsModel); diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts index 4cbffbca78b..2b14f5c8dd6 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 { Observable, of as observableOf, Subscription } from 'rxjs'; -import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +import { distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators'; import { AuthService } from '../../core/auth/auth.service'; import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model'; import { Collection } from '../../core/shared/collection.model'; @@ -131,6 +131,7 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { if ((changes.collectionId && this.collectionId) && (changes.submissionId && this.submissionId)) { this.isActive = true; + // retrieve submission's section list this.submissionSections = this.submissionService.getSubmissionObject(this.submissionId).pipe( filter(() => this.isActive), @@ -143,7 +144,9 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { } else { return observableOf([]); } - })); + }), + tap((res) => { console.log(res); })); + this.uploadEnabled$ = this.sectionsService.isSectionTypeAvailable(this.submissionId, SectionsType.Upload); // check if is submission loading @@ -230,6 +233,8 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { protected getSectionsList(): Observable { return this.submissionService.getSubmissionSections(this.submissionId).pipe( filter((sections: SectionDataObject[]) => isNotEmpty(sections)), - map((sections: SectionDataObject[]) => sections)); + map((sections: SectionDataObject[]) => sections + )); + } } diff --git a/src/app/submission/objects/submission-objects.effects.ts b/src/app/submission/objects/submission-objects.effects.ts index b4ba1c24809..7f6a690a5fb 100644 --- a/src/app/submission/objects/submission-objects.effects.ts +++ b/src/app/submission/objects/submission-objects.effects.ts @@ -125,8 +125,8 @@ export class SubmissionObjectEffects { this.submissionService.getSubmissionObjectLinkName(), action.payload.submissionId, 'sections').pipe( - map((response: SubmissionObject[]) => new SaveSubmissionFormSuccessAction(action.payload.submissionId, response, action.payload.isManual)), - catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); + map((response: SubmissionObject[]) => new SaveSubmissionFormSuccessAction(action.payload.submissionId, response, action.payload.isManual)), + catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); })); /** @@ -139,8 +139,8 @@ export class SubmissionObjectEffects { this.submissionService.getSubmissionObjectLinkName(), action.payload.submissionId, 'sections').pipe( - map((response: SubmissionObject[]) => new SaveForLaterSubmissionFormSuccessAction(action.payload.submissionId, response)), - catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); + map((response: SubmissionObject[]) => new SaveForLaterSubmissionFormSuccessAction(action.payload.submissionId, response)), + catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); })); /** @@ -179,8 +179,8 @@ export class SubmissionObjectEffects { action.payload.submissionId, 'sections', action.payload.sectionId).pipe( - map((response: SubmissionObject[]) => new SaveSubmissionSectionFormSuccessAction(action.payload.submissionId, response)), - catchError(() => observableOf(new SaveSubmissionSectionFormErrorAction(action.payload.submissionId)))); + map((response: SubmissionObject[]) => new SaveSubmissionSectionFormSuccessAction(action.payload.submissionId, response)), + catchError(() => observableOf(new SaveSubmissionSectionFormErrorAction(action.payload.submissionId)))); })); /** @@ -202,16 +202,16 @@ export class SubmissionObjectEffects { this.submissionService.getSubmissionObjectLinkName(), action.payload.submissionId, 'sections').pipe( - map((response: SubmissionObject[]) => { - if (this.canDeposit(response)) { - return new DepositSubmissionAction(action.payload.submissionId); - } else { - this.notificationsService.warning(null, this.translate.get('submission.sections.general.sections_not_valid')); - return this.parseSaveResponse((currentState.submission as SubmissionState).objects[action.payload.submissionId], - response, action.payload.submissionId, currentState.forms); - } - }), - catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); + map((response: SubmissionObject[]) => { + if (this.canDeposit(response)) { + return new DepositSubmissionAction(action.payload.submissionId); + } else { + this.notificationsService.warning(null, this.translate.get('submission.sections.general.sections_not_valid')); + return this.parseSaveResponse((currentState.submission as SubmissionState).objects[action.payload.submissionId], + response, action.payload.submissionId, currentState.forms); + } + }), + catchError(() => observableOf(new SaveSubmissionFormErrorAction(action.payload.submissionId)))); })); /** @@ -310,13 +310,13 @@ export class SubmissionObjectEffects { tap(() => this.notificationsService.error(null, this.translate.get('submission.sections.general.discard_error_notice')))); constructor(private actions$: Actions, - private notificationsService: NotificationsService, - private operationsService: SubmissionJsonPatchOperationsService, - private sectionService: SectionsService, - private store$: Store, - private submissionService: SubmissionService, - private submissionObjectService: SubmissionObjectDataService, - private translate: TranslateService) { + private notificationsService: NotificationsService, + private operationsService: SubmissionJsonPatchOperationsService, + private sectionService: SectionsService, + private store$: Store, + private submissionService: SubmissionService, + private submissionObjectService: SubmissionObjectDataService, + private translate: TranslateService) { } /** diff --git a/src/app/submission/sections/accesses/section-accesses.component.html b/src/app/submission/sections/accesses/section-accesses.component.html index b7acab843b4..db5f92013b8 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.html +++ b/src/app/submission/sections/accesses/section-accesses.component.html @@ -1 +1,2 @@ -

section-accesses works!

+ \ No newline at end of file diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts index cc8f1f1ab4e..4c2c5e400be 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.spec.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -1,25 +1,144 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormService } from './../../../shared/form/form.service'; +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; import { SubmissionSectionAccessesComponent } from './section-accesses.component'; +import { SectionsService } from 'src/app/submission/sections/sections.service'; +import { SectionsServiceStub } from 'src/app/shared/testing/sections-service.stub'; + +import { FormBuilderService } from 'src/app/shared/form/builder/form-builder.service'; +import { getMockFormBuilderService } from 'src/app/shared/mocks/form-builder-service.mock'; +import { SubmissionAccessesConfigService } from 'src/app/core/config/submission-accesses-config.service'; +import { getSubmissionAccessesConfigService } from 'src/app/shared/mocks/section-accesses-config.service.mock'; +import { SectionAccessesService } from 'src/app/submission/sections/accesses/section-accesses.service'; +import { SectionFormOperationsService } from 'src/app/submission/sections/form/section-form-operations.service'; +import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder'; +import { TranslateService, TranslateModule } from '@ngx-translate/core'; +import { SubmissionJsonPatchOperationsService } from 'src/app/core/submission/submission-json-patch-operations.service'; +import { getSectionAccessesService } from 'src/app/shared/mocks/section-accesses.service.mock'; +import { getMockFormOperationsService } from 'src/app/shared/mocks/form-operations-service.mock'; +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; +import { SubmissionJsonPatchOperationsServiceStub } from 'src/app/shared/testing/submission-json-patch-operations-service.stub'; +import { BrowserModule, By } from '@angular/platform-browser'; + +import { Observable, of as observableOf, observable } from 'rxjs'; +import { select, Store } from '@ngrx/store'; +import { FormComponent } from 'src/app/shared/form/form.component'; +import { DynamicFormControlModel, DynamicCheckboxModel, DynamicFormArrayGroupModel, DynamicSelectModel, DynamicDatePickerModel, DynamicFormArrayModel } from '@ng-dynamic-forms/core'; +import { AppState } from 'src/app/app.reducer'; +import { getMockFormService } from 'src/app/shared/mocks/form-service.mock'; +import { mockAccessesFormData } from 'src/app/shared/mocks/submission.mock'; +import { accessConditionChangeEvent, checkboxChangeEvent } from 'src/app/shared/testing/form-event.stub'; describe('SubmissionSectionAccessesComponent', () => { let component: SubmissionSectionAccessesComponent; let fixture: ComponentFixture; + const sectionsServiceStub = new SectionsServiceStub(); + // const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex); + + const builderService: FormBuilderService = getMockFormBuilderService(); + const submissionAccessesConfigService = getSubmissionAccessesConfigService(); + const sectionAccessesService = getSectionAccessesService(); + const sectionFormOperationsService = getMockFormOperationsService(); + const operationsBuilder = jasmine.createSpyObj('operationsBuilder', { + add: undefined, + remove: undefined, + replace: undefined, + }); + + let formService: any; + + const storeStub = jasmine.createSpyObj('store', ['dispatch']); + + const sectionData = { + header: 'submit.progressbar.accessCondition', + config: 'http://localhost:8080/server/api/config/submissionaccessoptions/AccessConditionDefaultConfiguration', + mandatory: true, + sectionType: 'accessCondition', + collapsed: false, + enabled: true, + data: { + discoverable: true, + accessConditions: [] + }, + errorsToShow: [], + serverValidationErrors: [], + isLoading: false, + isValid: true + }; + + + beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [SubmissionSectionAccessesComponent] + imports: [ + BrowserModule, + TranslateModule.forRoot() + ], + declarations: [SubmissionSectionAccessesComponent, FormComponent], + providers: [ + { provide: SectionsService, useValue: sectionsServiceStub }, + { provide: FormBuilderService, useValue: builderService }, + { provide: SubmissionAccessesConfigService, useValue: submissionAccessesConfigService }, + { provide: SectionAccessesService, useValue: sectionAccessesService }, + { provide: SectionFormOperationsService, useValue: sectionFormOperationsService }, + { provide: JsonPatchOperationsBuilder, useValue: operationsBuilder }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: FormService, useValue: getMockFormService() }, + { provide: Store, useValue: storeStub }, + { provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub }, + { provide: 'sectionDataProvider', useValue: sectionData }, + { provide: 'submissionIdProvider', useValue: '1508' }, + ] }) .compileComponents(); }); - beforeEach(() => { + beforeEach(inject([Store], (store: Store) => { fixture = TestBed.createComponent(SubmissionSectionAccessesComponent); component = fixture.componentInstance; + formService = TestBed.inject(FormService); + formService.validateAllFormFields.and.callFake(() => null); + formService.isValid.and.returnValue(observableOf(true)); + formService.getFormData.and.returnValue(observableOf(mockAccessesFormData)); fixture.detectChanges(); - }); + })); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have created formModel', () => { + expect(component.formModel).toBeTruthy(); + }); + + it('should have formModel length should be 2', () => { + expect(component.formModel.length).toEqual(2); + }); + + it('formModel should have 1 model type checkbox and 1 model type array', () => { + expect(component.formModel[0] instanceof DynamicCheckboxModel).toBeTrue(); + expect(component.formModel[1] instanceof DynamicFormArrayModel).toBeTrue(); + }); + + it('formModel type array should have formgroup with 1 input and 2 datepickers', () => { + const formModel: any = component.formModel[1]; + const formGroup = formModel.groupFactory()[0].group; + expect(formGroup[0] instanceof DynamicSelectModel).toBeTrue(); + expect(formGroup[1] instanceof DynamicDatePickerModel).toBeTrue(); + expect(formGroup[2] instanceof DynamicDatePickerModel).toBeTrue(); + }); + + it('when checkbox changed it should call operationsBuilder replace function', () => { + component.onChange(checkboxChangeEvent); + fixture.detectChanges(); + + expect(operationsBuilder.replace).toHaveBeenCalled(); + }); + + it('when dropdown select changed it should call operationsBuilder add function', () => { + component.onChange(accessConditionChangeEvent); + fixture.detectChanges(); + expect(operationsBuilder.add).toHaveBeenCalled(); + }); }); diff --git a/src/app/submission/sections/accesses/section-accesses.component.ts b/src/app/submission/sections/accesses/section-accesses.component.ts index 2d757f5e0c4..abd2e2802a8 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.ts @@ -1,20 +1,404 @@ -import { Component, OnInit } from '@angular/core'; +import { SectionAccessesService } from './section-accesses.service'; +import { Component, OnInit, ChangeDetectorRef, Inject, ViewChild } from '@angular/core'; import { renderSectionFor } from 'src/app/submission/sections/sections-decorator'; import { SectionsType } from 'src/app/submission/sections/sections-type'; +import { SubmissionService } from 'src/app/submission/submission.service'; +import { SectionDataObject } from 'src/app/submission/sections/models/section-data.model'; +import { SectionsService } from 'src/app/submission/sections/sections.service'; +import { SectionModelComponent } from 'src/app/submission/sections/models/section.model'; +import { Observable, of, Subscription, combineLatest as observableCombineLatest, BehaviorSubject, combineLatest } from 'rxjs'; +import { DynamicFormControlModel, MATCH_ENABLED, OR_OPERATOR, DynamicSelectModel, DynamicDatePickerModel, DynamicFormGroupModel, DynamicFormArrayModel, DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, DynamicCheckboxModel, DynamicFormControlEvent } from '@ng-dynamic-forms/core'; +import { FormBuilderService } from 'src/app/shared/form/builder/form-builder.service'; +import { findIndex, isEqual } from 'lodash'; + +import { + ACCESS_CONDITION_GROUP_CONFIG, + ACCESS_CONDITION_GROUP_LAYOUT, + ACCESS_CONDITIONS_FORM_ARRAY_CONFIG, + ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT, + FORM_ACCESS_CONDITION_END_DATE_CONFIG, + FORM_ACCESS_CONDITION_END_DATE_LAYOUT, + FORM_ACCESS_CONDITION_START_DATE_CONFIG, + FORM_ACCESS_CONDITION_START_DATE_LAYOUT, + FORM_ACCESS_CONDITION_TYPE_CONFIG, + FORM_ACCESS_CONDITION_TYPE_LAYOUT, + ACCESS_FORM_CHECKBOX_LAYOUT +} from './section-accesses.model'; +import { isNotEmpty, isNotUndefined, isUndefined, hasValue, isNotNull } from 'src/app/shared/empty.util'; +import { WorkspaceitemSectionAccessesObject } from '../../../core/submission/models/workspaceitem-section-accesses.model'; +import { SubmissionAccessesConfigService } from 'src/app/core/config/submission-accesses-config.service'; +import { getFirstSucceededRemoteData } from 'src/app/core/shared/operators'; +import { map, take, filter, mergeMap } from 'rxjs/operators'; +import { FormComponent } from 'src/app/shared/form/form.component'; +import { FormService } from 'src/app/shared/form/form.service'; +import { JsonPatchOperationPathCombiner } from 'src/app/core/json-patch/builder/json-patch-operation-path-combiner'; +import { SectionFormOperationsService } from 'src/app/submission/sections/form/section-form-operations.service'; +import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder'; +import { AccessesConditionOption } from 'src/app/core/config/models/config-accesses-conditions-options.model'; +import { TranslateService } from '@ngx-translate/core'; +import { FormFieldPreviousValueObject } from 'src/app/shared/form/builder/models/form-field-previous-value-object'; +import { environment } from 'src/environments/environment'; +import { SubmissionObject } from 'src/app/core/submission/models/submission-object.model'; +import { SubmissionJsonPatchOperationsService } from 'src/app/core/submission/submission-json-patch-operations.service'; +import { dateToISOFormat } from 'src/app/shared/date.util'; @Component({ selector: 'ds-section-accesses', templateUrl: './section-accesses.component.html', styleUrls: ['./section-accesses.component.scss'] }) -@renderSectionFor(SectionsType.Accesses) -export class SubmissionSectionAccessesComponent implements OnInit { +@renderSectionFor(SectionsType.AccessesCondition) +export class SubmissionSectionAccessesComponent extends SectionModelComponent { + + /** + * The FormComponent reference + */ + @ViewChild('formRef') public formRef: FormComponent; + + /** + * List of available access conditions that could be set to files + */ + public availableAccessConditionOptions: AccessesConditionOption[]; // List of accessConditions that an user can select + + /** + * The form id + * @type {string} + */ + public formId: string; + + /** + * The accesses metadata data + * @type {WorkspaceitemSectionAccessesObject} + */ + public accessesData: WorkspaceitemSectionAccessesObject; + + /** + * Array to track all subscriptions and unsubscribe them onDestroy + * @type {Array} + */ + protected subs: Subscription[] = []; + + + /** + * The collection name this submission belonging to + * @type {string} + */ + public collectionName: string; + + /** + * Is the upload required + * @type {boolean} + */ + public required$ = new BehaviorSubject(true); + + /** + * The [[JsonPatchOperationPathCombiner]] object + * @type {JsonPatchOperationPathCombiner} + */ + protected pathCombiner: JsonPatchOperationPathCombiner; + + /** + * A map representing all field prevous values + * @type {Map} + */ + protected previousValue: Map = new Map(); + + /** + * A map representing all field on their way to be removed + * @type {Map} + */ + protected fieldsOnTheirWayToBeRemoved: Map = new Map(); + + /** + * The form model + * @type {DynamicFormControlModel[]} + */ + formModel: DynamicFormControlModel[]; + + /** + * Initialize instance variables + * + * @param {SectionsService} sectionService + * @param {SectionDataObject} injectedSectionData + * @param {FormService} formService + * @param {JsonPatchOperationsBuilder} operationsBuilder + * @param {SectionFormOperationsService} formOperationsService + * @param {FormBuilderService} formBuilderService + * @param {SubmissionAccessesConfigService} accessesConfigService + * @param {SectionAccessesService} accessesService + * @param {SubmissionJsonPatchOperationsService} operationsService + * @param {string} injectedSubmissionId + */ + constructor( + protected sectionService: SectionsService, + private formBuilderService: FormBuilderService, + private accessesConfigService: SubmissionAccessesConfigService, + private accessesService: SectionAccessesService, + protected formOperationsService: SectionFormOperationsService, + protected operationsBuilder: JsonPatchOperationsBuilder, + private formService: FormService, + private translate: TranslateService, + private operationsService: SubmissionJsonPatchOperationsService, + @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, + @Inject('submissionIdProvider') public injectedSubmissionId: string) { + super(undefined, injectedSectionData, injectedSubmissionId); + } + + /** + * Initialize all instance variables and retrieve collection default access conditions + */ + protected onSectionInit(): void { + + this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id); + this.formId = this.formService.getUniqueId(this.sectionData.id); + const config$ = this.accessesConfigService.findByHref(this.sectionData.config, true, false).pipe( + getFirstSucceededRemoteData(), + map((config) => config.payload), + ); + + const accessData$ = this.accessesService.getAccessesData(this.submissionId, this.sectionData.id); + + combineLatest(config$, accessData$).subscribe(([config, accessData]) => { + this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : []; + this.accessesData = accessData; + this.formModel = this.buildFileEditForm(); + }); + + + } + + /** + * Get section status + * + * @return Observable + * the section status + */ + protected getSectionStatus(): Observable { + console.log('Method not implemented.'); + return of(true); + } + + /** + * Initialize form model + */ + protected buildFileEditForm() { + + const formModel: DynamicFormControlModel[] = []; + formModel.push( + new DynamicCheckboxModel({ + id: 'discoverable', + label: this.translate.instant('submission.sections.accesses.form.discoverable-label'), + name: 'discoverable', + value: this.accessesData.discoverable + }) + ); - // tslint:disable-next-line:no-empty - constructor() { } + const accessConditionTypeModelConfig = Object.assign({}, FORM_ACCESS_CONDITION_TYPE_CONFIG); + const accessConditionsArrayConfig = Object.assign({}, ACCESS_CONDITIONS_FORM_ARRAY_CONFIG); + const accessConditionTypeOptions = []; - // tslint:disable-next-line:no-empty - ngOnInit(): void { + for (const accessCondition of this.availableAccessConditionOptions) { + accessConditionTypeOptions.push( + { + label: accessCondition.name, + value: accessCondition.name + } + ); + } + accessConditionTypeModelConfig.options = accessConditionTypeOptions; + + // Dynamically assign of relation in config. For startdate, endDate, groups. + const hasStart = []; + const hasEnd = []; + const hasGroups = []; + this.availableAccessConditionOptions.forEach((condition) => { + const showStart: boolean = condition.hasStartDate === true; + const showEnd: boolean = condition.hasEndDate === true; + const showGroups: boolean = showStart || showEnd; + if (showStart) { + hasStart.push({ id: 'name', value: condition.name }); + } + if (showEnd) { + hasEnd.push({ id: 'name', value: condition.name }); + } + if (showGroups) { + hasGroups.push({ id: 'name', value: condition.name }); + } + }); + const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart }] }; + const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd }] }; + + accessConditionsArrayConfig.groupFactory = () => { + const type = new DynamicSelectModel(accessConditionTypeModelConfig, FORM_ACCESS_CONDITION_TYPE_LAYOUT); + const startDateConfig = Object.assign({}, FORM_ACCESS_CONDITION_START_DATE_CONFIG, confStart); + const endDateConfig = Object.assign({}, FORM_ACCESS_CONDITION_END_DATE_CONFIG, confEnd); + + const startDate = new DynamicDatePickerModel(startDateConfig, FORM_ACCESS_CONDITION_START_DATE_LAYOUT); + const endDate = new DynamicDatePickerModel(endDateConfig, FORM_ACCESS_CONDITION_END_DATE_LAYOUT); + const accessConditionGroupConfig = Object.assign({}, ACCESS_CONDITION_GROUP_CONFIG); + accessConditionGroupConfig.group = [type]; + if (hasStart.length > 0) { accessConditionGroupConfig.group.push(startDate); } + if (hasEnd.length > 0) { accessConditionGroupConfig.group.push(endDate); } + return [new DynamicFormGroupModel(accessConditionGroupConfig, ACCESS_CONDITION_GROUP_LAYOUT)]; + }; + + // Number of access conditions blocks in form + accessConditionsArrayConfig.initialCount = isNotEmpty(this.accessesData.accessConditions) ? this.accessesData.accessConditions.length : 1; + formModel.push( + new DynamicFormArrayModel(accessConditionsArrayConfig, ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT) + ); + + this.initModelData(formModel); + return formModel; + } + + /** + * Initialize form model values + * + * @param formModel + * The form model + */ + public initModelData(formModel: DynamicFormControlModel[]) { + this.accessesData.accessConditions.forEach((accessCondition, index) => { + Array.of('name', 'startDate', 'endDate') + .filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key])) + .forEach((key) => { + const metadataModel: any = this.formBuilderService.findById(key, formModel, index); + if (metadataModel) { + if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) { + const date = new Date(accessCondition[key]); + metadataModel.value = { + year: date.getUTCFullYear(), + month: date.getUTCMonth() + 1, + day: date.getUTCDate() + }; + } else { + metadataModel.value = accessCondition[key]; + } + } + }); + }); + } + + onSubmit() { + // this.formService.validateAllFormFields(this.formRef.formGroup); + } + + /** + * Method called when a form dfChange event is fired. + * Dispatch form operations based on changes. + */ + onChange(event: DynamicFormControlEvent) { + if (event.model.type === 'CHECKBOX') { + const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event); + const value = this.formOperationsService.getFieldValueFromChangeEvent(event); + this.operationsBuilder.replace(this.pathCombiner.getPath(path), value.value, true); + } else { + // validate form + this.formService.validateAllFormFields(this.formRef.formGroup); + this.formService.isValid(this.formId).pipe( + take(1), + filter((isValid) => isValid), + mergeMap(() => this.formService.getFormData(this.formId)), + take(1), + mergeMap((formData: any) => { + console.log(formData); + const accessConditionsToSave = []; + formData.accessCondition + .map((accessConditions) => accessConditions.accessConditionGroup) + .filter((accessCondition) => isNotEmpty(accessCondition)) + .forEach((accessCondition) => { + let accessConditionOpt; + + this.availableAccessConditionOptions + .filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value) + .forEach((element) => accessConditionOpt = element); + + if (accessConditionOpt) { + const currentAccessCondition = Object.assign({}, accessCondition); + currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name); + + /* When start and end date fields are deactivated, their values may be still present in formData, + therefore it is necessary to delete them if they're not allowed by the current access condition option. */ + if (!accessConditionOpt.hasStartDate) { + delete currentAccessCondition.startDate; + } else if (accessCondition.startDate) { + const startDate = this.retrieveValueFromField(accessCondition.startDate); + currentAccessCondition.startDate = dateToISOFormat(startDate); + } + if (!accessConditionOpt.hasEndDate) { + delete currentAccessCondition.endDate; + } else if (accessCondition.endDate) { + const endDate = this.retrieveValueFromField(accessCondition.endDate); + currentAccessCondition.endDate = dateToISOFormat(endDate); + } + accessConditionsToSave.push(currentAccessCondition); + } + }); + + this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true); + + return this.formService.getFormData(this.formId); + + }) + ).subscribe((result: SubmissionObject[]) => { + console.log(result); + }); + } + } + + protected retrieveValueFromField(field: any) { + const temp = Array.isArray(field) ? field[0] : field; + return (temp) ? temp.value : undefined; + } + /** + * 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)) { + return this.sectionData.data.hasOwnProperty(fieldId) && + isNotEmpty(this.sectionData.data[fieldId][index]) && + !this.isFieldToRemove(fieldId, index); + } else { + return false; + } } + private hasRelatedCustomError(medatata): boolean { + const index = findIndex(this.sectionData.errorsToShow, { path: this.pathCombiner.getPath(medatata).path }); + if (index !== -1) { + const error = this.sectionData.errorsToShow[index]; + const validator = error.message.replace('error.validation.', ''); + return !environment.form.validatorMap.hasOwnProperty(validator); + } else { + return false; + } + } + + /** + * Check if the specified field is on the way to be removed + * + * @param fieldId + * the section data retrieved from the serverù + * @param index + * the section data retrieved from the server + */ + isFieldToRemove(fieldId, index) { + return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index); + } + + + /** + * Unsubscribe from all subscriptions + */ + onSectionDestroy() { + this.subs + .filter((subscription) => hasValue(subscription)) + .forEach((subscription) => subscription.unsubscribe()); + } } diff --git a/src/app/submission/sections/accesses/section-accesses.model.ts b/src/app/submission/sections/accesses/section-accesses.model.ts new file mode 100644 index 00000000000..6b8178f9d5e --- /dev/null +++ b/src/app/submission/sections/accesses/section-accesses.model.ts @@ -0,0 +1,117 @@ +import { + DynamicDatePickerModelConfig, + DynamicFormArrayModelConfig, + DynamicFormControlLayout, + DynamicFormGroupModelConfig, + DynamicSelectModelConfig, + MATCH_ENABLED, + OR_OPERATOR, +} from '@ng-dynamic-forms/core'; + + +export const ACCESS_FORM_CHECKBOX_LAYOUT: DynamicFormControlLayout = { + element: { + host: 'form-group flex-fill access-condition-group', + id: 'discoverable', + // disabled: false, + label: 'submission.sections.accesses.form.discoverable-label', + name: 'discoverable', + } +}; + + + +export const ACCESS_CONDITION_GROUP_CONFIG: DynamicFormGroupModelConfig = { + id: 'accessConditionGroup', + group: [] +}; + +export const ACCESS_CONDITION_GROUP_LAYOUT: DynamicFormControlLayout = { + element: { + host: 'form-group flex-fill access-condition-group', + container: 'pl-1 pr-1', + control: 'form-row ' + } +}; + +export const ACCESS_CONDITIONS_FORM_ARRAY_CONFIG: DynamicFormArrayModelConfig = { + id: 'accessCondition', + groupFactory: null, +}; +export const ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT: DynamicFormControlLayout = { + grid: { + group: 'form-row', + } +}; + +export const FORM_ACCESS_CONDITION_TYPE_CONFIG: DynamicSelectModelConfig = { + id: 'name', + label: 'submission.sections.upload.form.access-condition-label', + options: [] +}; +export const FORM_ACCESS_CONDITION_TYPE_LAYOUT: DynamicFormControlLayout = { + element: { + host: 'col-12', + label: 'col-form-label name-label' + } +}; + +export const FORM_ACCESS_CONDITION_START_DATE_CONFIG: DynamicDatePickerModelConfig = { + id: 'startDate', + label: 'submission.sections.upload.form.from-label', + placeholder: 'submission.sections.upload.form.from-placeholder', + inline: false, + toggleIcon: 'far fa-calendar-alt', + relations: [ + { + match: MATCH_ENABLED, + operator: OR_OPERATOR, + when: [] + } + ], + required: true, + validators: { + required: null + }, + errorMessages: { + required: 'submission.sections.upload.form.date-required-from' + } +}; +export const FORM_ACCESS_CONDITION_START_DATE_LAYOUT: DynamicFormControlLayout = { + element: { + label: 'col-form-label' + }, + grid: { + host: 'col-6' + } +}; + +export const FORM_ACCESS_CONDITION_END_DATE_CONFIG: DynamicDatePickerModelConfig = { + id: 'endDate', + label: 'submission.sections.upload.form.until-label', + placeholder: 'submission.sections.upload.form.until-placeholder', + inline: false, + toggleIcon: 'far fa-calendar-alt', + relations: [ + { + match: MATCH_ENABLED, + operator: OR_OPERATOR, + when: [] + } + ], + required: true, + validators: { + required: null + }, + errorMessages: { + required: 'submission.sections.upload.form.date-required-until' + } +}; +export const FORM_ACCESS_CONDITION_END_DATE_LAYOUT: DynamicFormControlLayout = { + element: { + label: 'col-form-label' + }, + grid: { + host: 'col-6' + } +}; diff --git a/src/app/submission/sections/accesses/section-accesses.service.ts b/src/app/submission/sections/accesses/section-accesses.service.ts new file mode 100644 index 00000000000..04867bbd4b0 --- /dev/null +++ b/src/app/submission/sections/accesses/section-accesses.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; + +import { Observable, of } from 'rxjs'; +import { distinctUntilChanged, filter, map } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; + +import { SubmissionState } from '../../submission.reducers'; +import { isUndefined } from 'util'; +import { submissionSectionFromIdSelector, submissionSectionDataFromIdSelector } from 'src/app/submission/selectors'; + + +/** + * A service that provides methods to handle submission's bitstream state. + */ +@Injectable() +export class SectionAccessesService { + + /** + * Initialize service variables + * + * @param {Store} store + */ + constructor(private store: Store) { } + + + /** + * 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 getAccessesData(submissionId: string, sectionId: string): Observable { + + return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe( + filter((state) => !isUndefined(state)), + distinctUntilChanged()); + } +} diff --git a/src/app/submission/sections/container/section-container.component.html b/src/app/submission/sections/container/section-container.component.html index c510c7ddf1d..d4b289a8544 100644 --- a/src/app/submission/sections/container/section-container.component.html +++ b/src/app/submission/sections/container/section-container.component.html @@ -1,52 +1,36 @@ -
- + \ No newline at end of file diff --git a/src/app/submission/sections/container/section-container.component.ts b/src/app/submission/sections/container/section-container.component.ts index f040288667c..8f9ebfbfda7 100644 --- a/src/app/submission/sections/container/section-container.component.ts +++ b/src/app/submission/sections/container/section-container.component.ts @@ -64,9 +64,9 @@ export class SubmissionSectionContainerComponent implements OnInit { ngOnInit() { this.objectInjector = Injector.create({ providers: [ - {provide: 'collectionIdProvider', useFactory: () => (this.collectionId), deps: []}, - {provide: 'sectionDataProvider', useFactory: () => (this.sectionData), deps: []}, - {provide: 'submissionIdProvider', useFactory: () => (this.submissionId), deps: []}, + { provide: 'collectionIdProvider', useFactory: () => (this.collectionId), deps: [] }, + { provide: 'sectionDataProvider', useFactory: () => (this.sectionData), deps: [] }, + { provide: 'submissionIdProvider', useFactory: () => (this.submissionId), deps: [] }, ], parent: this.injector }); diff --git a/src/app/submission/sections/models/section.model.ts b/src/app/submission/sections/models/section.model.ts index e4ef837903b..c9be0792134 100644 --- a/src/app/submission/sections/models/section.model.ts +++ b/src/app/submission/sections/models/section.model.ts @@ -59,8 +59,8 @@ export abstract class SectionModelComponent implements OnDestroy, OnInit, Sectio * @param {string} injectedSubmissionId */ public constructor(@Inject('collectionIdProvider') public injectedCollectionId: string, - @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, - @Inject('submissionIdProvider') public injectedSubmissionId: string) { + @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, + @Inject('submissionIdProvider') public injectedSubmissionId: string) { this.collectionId = injectedCollectionId; this.sectionData = injectedSectionData; this.submissionId = injectedSubmissionId; diff --git a/src/app/submission/sections/sections-type.ts b/src/app/submission/sections/sections-type.ts index 37b7cc5e745..d13aef1da1d 100644 --- a/src/app/submission/sections/sections-type.ts +++ b/src/app/submission/sections/sections-type.ts @@ -5,5 +5,5 @@ export enum SectionsType { License = 'license', CcLicense = 'cclicense', collection = 'collection', - Accesses = 'accesses', + AccessesCondition = 'accessCondition', } diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 53358d48e27..9fd005a8527 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -174,10 +174,10 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { this.subscriptions.push( this.uploadService .getFileData(this.submissionId, this.sectionId, this.fileId).pipe( - filter((bitstream) => isNotUndefined(bitstream))) + filter((bitstream) => isNotUndefined(bitstream))) .subscribe((bitstream) => { - this.fileData = bitstream; - } + this.fileData = bitstream; + } ) ); } @@ -251,12 +251,12 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { protected loadFormMetadata() { this.configMetadataForm.rows.forEach((row) => { - row.fields.forEach((field) => { - field.selectableMetadata.forEach((metadatum) => { - this.formMetadata.push(metadatum.metadata); - }); + row.fields.forEach((field) => { + field.selectableMetadata.forEach((metadatum) => { + this.formMetadata.push(metadatum.metadata); }); - } + }); + } ); } diff --git a/src/app/submission/selectors.ts b/src/app/submission/selectors.ts index df337323812..09f1bae3fe2 100644 --- a/src/app/submission/selectors.ts +++ b/src/app/submission/selectors.ts @@ -45,27 +45,28 @@ export function submissionUploadedFilesFromIdSelector(submissionId: string, sect } export function submissionUploadedFileFromUuidSelector(submissionId: string, sectionId: string, uuid: string): MemoizedSelector { - const filesSelector = submissionSectionDataFromIdSelector(submissionId, sectionId); + const filesSelector = submissionSectionDataFromIdSelector(submissionId, sectionId); return keySelector(filesSelector, 'files', uuid); } export function submissionSectionFromIdSelector(submissionId: string, sectionId: string): MemoizedSelector { - const submissionIdSelector = submissionObjectFromIdSelector(submissionId); + const submissionIdSelector = submissionObjectFromIdSelector(submissionId); return keySelector(submissionIdSelector, 'sections', sectionId); } export function submissionSectionDataFromIdSelector(submissionId: string, sectionId: string): MemoizedSelector { - const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId); + const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId); return subStateSelector(submissionIdSelector, 'data'); } export function submissionSectionErrorsFromIdSelector(submissionId: string, sectionId: string): MemoizedSelector { - const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId); + const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId); return subStateSelector(submissionIdSelector, 'errorsToShow'); } export function submissionSectionServerErrorsFromIdSelector(submissionId: string, sectionId: string): MemoizedSelector { - const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId); + const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId); return subStateSelector(submissionIdSelector, 'serverValidationErrors'); } + diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 13cf2016ddd..baf91d651c8 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -37,6 +37,9 @@ import { ResearchEntitiesModule } from '../entity-groups/research-entities/resea import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component'; import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component'; import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component'; +import { SubmissionSectionAccessesComponent } from 'src/app/submission/sections/accesses/section-accesses.component'; +import { SubmissionAccessesConfigService } from 'src/app/core/config/submission-accesses-config.service'; +import { SectionAccessesService } from 'src/app/submission/sections/accesses/section-accesses.service'; const DECLARATIONS = [ SubmissionSectionUploadAccessConditionsComponent, @@ -62,7 +65,8 @@ const DECLARATIONS = [ ThemedSubmissionImportExternalComponent, SubmissionImportExternalSearchbarComponent, SubmissionImportExternalPreviewComponent, - SubmissionImportExternalCollectionComponent + SubmissionImportExternalCollectionComponent, + SubmissionSectionAccessesComponent ]; @NgModule({ @@ -80,7 +84,9 @@ const DECLARATIONS = [ providers: [ SectionUploadService, SectionsService, - SubmissionUploadsConfigService + SubmissionUploadsConfigService, + SubmissionAccessesConfigService, + SectionAccessesService ] }) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5cd42bc24c5..9ada3f073f6 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3889,7 +3889,8 @@ "submission.sections.upload.upload-successful": "Upload successful", - + "submission.sections.accesses.form.discoverable-label": "Discoverable", + "submission.submit.breadcrumbs": "New submission", From db25e27bff2006b6de7c3cb834193b68cacbf431 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 23 Dec 2021 16:31:59 +0100 Subject: [PATCH 03/19] [CST-4506] fix absolute paths --- src/app/core/core.module.ts | 8 +- .../models/submission-accesses.model.ts | 2 +- .../section-accesses-config.service.mock.ts | 4 +- .../mocks/section-accesses.service.mock.ts | 2 +- .../edit/submission-edit.component.ts | 17 +- .../form/submission-form.component.ts | 11 +- .../accesses/section-accesses.component.html | 8 +- .../section-accesses.component.spec.ts | 43 +- .../accesses/section-accesses.component.ts | 368 +++++++++--------- .../accesses/section-accesses.service.ts | 8 +- src/app/submission/submission.module.ts | 6 +- src/assets/i18n/en.json5 | 4 +- 12 files changed, 229 insertions(+), 252 deletions(-) diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 09f8616f9ec..fdccbf82f62 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -2,11 +2,7 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; -import { - DynamicFormLayoutService, - DynamicFormService, - DynamicFormValidationService -} from '@ng-dynamic-forms/core'; +import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { EffectsModule } from '@ngrx/effects'; import { Action, StoreConfig, StoreModule } from '@ngrx/store'; @@ -163,7 +159,7 @@ import { RootDataService } from './data/root-data.service'; import { Root } from './data/root.model'; import { SearchConfig } from './shared/search/search-filters/search-config.model'; import { SequenceService } from './shared/sequence.service'; -import { SubmissionAccessesModel } from 'src/app/core/config/models/config-submission-accesses.model'; +import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; /** * When not in production, endpoint responses can be mocked for testing purposes diff --git a/src/app/core/submission/models/submission-accesses.model.ts b/src/app/core/submission/models/submission-accesses.model.ts index 2affe641f5f..afad90b2ff4 100644 --- a/src/app/core/submission/models/submission-accesses.model.ts +++ b/src/app/core/submission/models/submission-accesses.model.ts @@ -3,7 +3,7 @@ import { typedObject } from '../../cache/builders/build-decorators'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { ResourceType } from '../../shared/resource-type'; import { HALResource } from '../../shared/hal-resource.model'; -import { SUBMISSION_ACCESSES } from 'src/app/core/submission/models/submission-accesses.resource-type'; +import { SUBMISSION_ACCESSES } from './submission-accesses.resource-type'; @typedObject @inheritSerialization(HALResource) diff --git a/src/app/shared/mocks/section-accesses-config.service.mock.ts b/src/app/shared/mocks/section-accesses-config.service.mock.ts index d26420443ec..0362493035b 100644 --- a/src/app/shared/mocks/section-accesses-config.service.mock.ts +++ b/src/app/shared/mocks/section-accesses-config.service.mock.ts @@ -1,6 +1,6 @@ import { SubmissionFormsConfigService } from '../../core/config/submission-forms-config.service'; -import { SubmissionFormsModel } from 'src/app/core/config/models/config-submission-forms.model'; -import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils'; +import { SubmissionFormsModel } from '../../core/config/models/config-submission-forms.model'; +import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; const configRes = Object.assign(new SubmissionFormsModel(), { 'id': 'AccessConditionDefaultConfiguration', diff --git a/src/app/shared/mocks/section-accesses.service.mock.ts b/src/app/shared/mocks/section-accesses.service.mock.ts index 5f2f5defc14..5302b37a633 100644 --- a/src/app/shared/mocks/section-accesses.service.mock.ts +++ b/src/app/shared/mocks/section-accesses.service.mock.ts @@ -1,4 +1,4 @@ -import { SubmissionFormsModel } from 'src/app/core/config/models/config-submission-forms.model'; +import { SubmissionFormsModel } from '../../core/config/models/config-submission-forms.model'; import { of as observableOf } from 'rxjs'; const dataRes = Object.assign(new SubmissionFormsModel(), { diff --git a/src/app/submission/edit/submission-edit.component.ts b/src/app/submission/edit/submission-edit.component.ts index 5512d054dcb..9a35c2dfe18 100644 --- a/src/app/submission/edit/submission-edit.component.ts +++ b/src/app/submission/edit/submission-edit.component.ts @@ -6,7 +6,6 @@ import { debounceTime, filter, switchMap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model'; -import { WorkspaceitemSectionAccessesObject } from '../../core/submission/models/workspaceitem-section-accesses.model'; import { hasValue, isEmpty, isNotEmptyOperator, isNotNull } from '../../shared/empty.util'; import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model'; import { SubmissionService } from '../submission.service'; @@ -97,13 +96,13 @@ export class SubmissionEditComponent implements OnDestroy, OnInit { * @param {SubmissionJsonPatchOperationsService} submissionJsonPatchOperationsService */ constructor(private changeDetectorRef: ChangeDetectorRef, - private notificationsService: NotificationsService, - private route: ActivatedRoute, - private router: Router, - private itemDataService: ItemDataService, - private submissionService: SubmissionService, - private translate: TranslateService, - private submissionJsonPatchOperationsService: SubmissionJsonPatchOperationsService) { + private notificationsService: NotificationsService, + private route: ActivatedRoute, + private router: Router, + private itemDataService: ItemDataService, + private submissionService: SubmissionService, + private translate: TranslateService, + private submissionJsonPatchOperationsService: SubmissionJsonPatchOperationsService) { } /** @@ -127,8 +126,6 @@ export class SubmissionEditComponent implements OnDestroy, OnInit { this.collectionId = (submissionObjectRD.payload.collection as Collection).id; this.selfUrl = submissionObjectRD.payload._links.self.href; this.sections = submissionObjectRD.payload.sections; - - this.itemLink$.next(submissionObjectRD.payload._links.item.href); this.item = submissionObjectRD.payload.item; this.submissionDefinition = (submissionObjectRD.payload.submissionDefinition as SubmissionDefinitionsModel); diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts index 2b14f5c8dd6..4cbffbca78b 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 { Observable, of as observableOf, Subscription } from 'rxjs'; -import { distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators'; +import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; import { AuthService } from '../../core/auth/auth.service'; import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model'; import { Collection } from '../../core/shared/collection.model'; @@ -131,7 +131,6 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { if ((changes.collectionId && this.collectionId) && (changes.submissionId && this.submissionId)) { this.isActive = true; - // retrieve submission's section list this.submissionSections = this.submissionService.getSubmissionObject(this.submissionId).pipe( filter(() => this.isActive), @@ -144,9 +143,7 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { } else { return observableOf([]); } - }), - tap((res) => { console.log(res); })); - + })); this.uploadEnabled$ = this.sectionsService.isSectionTypeAvailable(this.submissionId, SectionsType.Upload); // check if is submission loading @@ -233,8 +230,6 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy { protected getSectionsList(): Observable { return this.submissionService.getSubmissionSections(this.submissionId).pipe( filter((sections: SectionDataObject[]) => isNotEmpty(sections)), - map((sections: SectionDataObject[]) => sections - )); - + map((sections: SectionDataObject[]) => sections)); } } diff --git a/src/app/submission/sections/accesses/section-accesses.component.html b/src/app/submission/sections/accesses/section-accesses.component.html index db5f92013b8..6ae8d9160b4 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.html +++ b/src/app/submission/sections/accesses/section-accesses.component.html @@ -1,2 +1,6 @@ - \ No newline at end of file + diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts index 4c2c5e400be..669d905ec29 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.spec.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -1,29 +1,34 @@ -import { FormService } from './../../../shared/form/form.service'; -import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; +import { FormService } from '../../../shared/form/form.service'; +import { ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { SubmissionSectionAccessesComponent } from './section-accesses.component'; -import { SectionsService } from 'src/app/submission/sections/sections.service'; -import { SectionsServiceStub } from 'src/app/shared/testing/sections-service.stub'; - -import { FormBuilderService } from 'src/app/shared/form/builder/form-builder.service'; -import { getMockFormBuilderService } from 'src/app/shared/mocks/form-builder-service.mock'; -import { SubmissionAccessesConfigService } from 'src/app/core/config/submission-accesses-config.service'; -import { getSubmissionAccessesConfigService } from 'src/app/shared/mocks/section-accesses-config.service.mock'; -import { SectionAccessesService } from 'src/app/submission/sections/accesses/section-accesses.service'; -import { SectionFormOperationsService } from 'src/app/submission/sections/form/section-form-operations.service'; -import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder'; -import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { SubmissionJsonPatchOperationsService } from 'src/app/core/submission/submission-json-patch-operations.service'; -import { getSectionAccessesService } from 'src/app/shared/mocks/section-accesses.service.mock'; +import { SectionsService } from '../sections.service'; +import { SectionsServiceStub } from '../../../shared/testing/sections-service.stub'; + +import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; +import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock'; +import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service'; +import { getSubmissionAccessesConfigService } from '../../../shared/mocks/section-accesses-config.service.mock'; +import { SectionAccessesService } from './section-accesses.service'; +import { SectionFormOperationsService } from '../form/section-form-operations.service'; +import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service'; +import { getSectionAccessesService } from '../../../shared/mocks/section-accesses.service.mock'; import { getMockFormOperationsService } from 'src/app/shared/mocks/form-operations-service.mock'; import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; import { SubmissionJsonPatchOperationsServiceStub } from 'src/app/shared/testing/submission-json-patch-operations-service.stub'; -import { BrowserModule, By } from '@angular/platform-browser'; +import { BrowserModule } from '@angular/platform-browser'; -import { Observable, of as observableOf, observable } from 'rxjs'; -import { select, Store } from '@ngrx/store'; +import { of as observableOf } from 'rxjs'; +import { Store } from '@ngrx/store'; import { FormComponent } from 'src/app/shared/form/form.component'; -import { DynamicFormControlModel, DynamicCheckboxModel, DynamicFormArrayGroupModel, DynamicSelectModel, DynamicDatePickerModel, DynamicFormArrayModel } from '@ng-dynamic-forms/core'; +import { + DynamicCheckboxModel, + DynamicDatePickerModel, + DynamicFormArrayModel, + DynamicSelectModel +} from '@ng-dynamic-forms/core'; import { AppState } from 'src/app/app.reducer'; import { getMockFormService } from 'src/app/shared/mocks/form-service.mock'; import { mockAccessesFormData } from 'src/app/shared/mocks/submission.mock'; diff --git a/src/app/submission/sections/accesses/section-accesses.component.ts b/src/app/submission/sections/accesses/section-accesses.component.ts index abd2e2802a8..a1113501ee6 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.ts @@ -1,15 +1,25 @@ import { SectionAccessesService } from './section-accesses.service'; -import { Component, OnInit, ChangeDetectorRef, Inject, ViewChild } from '@angular/core'; -import { renderSectionFor } from 'src/app/submission/sections/sections-decorator'; -import { SectionsType } from 'src/app/submission/sections/sections-type'; -import { SubmissionService } from 'src/app/submission/submission.service'; -import { SectionDataObject } from 'src/app/submission/sections/models/section-data.model'; -import { SectionsService } from 'src/app/submission/sections/sections.service'; -import { SectionModelComponent } from 'src/app/submission/sections/models/section.model'; -import { Observable, of, Subscription, combineLatest as observableCombineLatest, BehaviorSubject, combineLatest } from 'rxjs'; -import { DynamicFormControlModel, MATCH_ENABLED, OR_OPERATOR, DynamicSelectModel, DynamicDatePickerModel, DynamicFormGroupModel, DynamicFormArrayModel, DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, DynamicCheckboxModel, DynamicFormControlEvent } from '@ng-dynamic-forms/core'; -import { FormBuilderService } from 'src/app/shared/form/builder/form-builder.service'; -import { findIndex, isEqual } from 'lodash'; +import { Component, Inject, ViewChild } from '@angular/core'; +import { renderSectionFor } from '../sections-decorator'; +import { SectionsType } from '../sections-type'; +import { SectionDataObject } from '../models/section-data.model'; +import { SectionsService } from '../sections.service'; +import { SectionModelComponent } from '../models/section.model'; +import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; +import { + DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX, + DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, + DynamicCheckboxModel, + DynamicDatePickerModel, + DynamicFormArrayModel, + DynamicFormControlEvent, + DynamicFormControlModel, + DynamicFormGroupModel, + DynamicSelectModel, + MATCH_ENABLED, + OR_OPERATOR +} from '@ng-dynamic-forms/core'; +import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { ACCESS_CONDITION_GROUP_CONFIG, @@ -21,26 +31,22 @@ import { FORM_ACCESS_CONDITION_START_DATE_CONFIG, FORM_ACCESS_CONDITION_START_DATE_LAYOUT, FORM_ACCESS_CONDITION_TYPE_CONFIG, - FORM_ACCESS_CONDITION_TYPE_LAYOUT, - ACCESS_FORM_CHECKBOX_LAYOUT + FORM_ACCESS_CONDITION_TYPE_LAYOUT } from './section-accesses.model'; -import { isNotEmpty, isNotUndefined, isUndefined, hasValue, isNotNull } from 'src/app/shared/empty.util'; +import { hasValue, isNotEmpty, isNotNull } from '../../../shared/empty.util'; import { WorkspaceitemSectionAccessesObject } from '../../../core/submission/models/workspaceitem-section-accesses.model'; -import { SubmissionAccessesConfigService } from 'src/app/core/config/submission-accesses-config.service'; -import { getFirstSucceededRemoteData } from 'src/app/core/shared/operators'; -import { map, take, filter, mergeMap } from 'rxjs/operators'; -import { FormComponent } from 'src/app/shared/form/form.component'; -import { FormService } from 'src/app/shared/form/form.service'; -import { JsonPatchOperationPathCombiner } from 'src/app/core/json-patch/builder/json-patch-operation-path-combiner'; -import { SectionFormOperationsService } from 'src/app/submission/sections/form/section-form-operations.service'; -import { JsonPatchOperationsBuilder } from 'src/app/core/json-patch/builder/json-patch-operations-builder'; -import { AccessesConditionOption } from 'src/app/core/config/models/config-accesses-conditions-options.model'; +import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service'; +import { getFirstSucceededRemoteData } from '../../../core/shared/operators'; +import { filter, map, mergeMap, take } from 'rxjs/operators'; +import { FormComponent } from '../../../shared/form/form.component'; +import { FormService } from '../../../shared/form/form.service'; +import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { SectionFormOperationsService } from '../form/section-form-operations.service'; +import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; +import { AccessesConditionOption } from '../../../core/config/models/config-accesses-conditions-options.model'; import { TranslateService } from '@ngx-translate/core'; -import { FormFieldPreviousValueObject } from 'src/app/shared/form/builder/models/form-field-previous-value-object'; -import { environment } from 'src/environments/environment'; -import { SubmissionObject } from 'src/app/core/submission/models/submission-object.model'; -import { SubmissionJsonPatchOperationsService } from 'src/app/core/submission/submission-json-patch-operations.service'; -import { dateToISOFormat } from 'src/app/shared/date.util'; +import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service'; +import { dateToISOFormat } from '../../../shared/date.util'; @Component({ selector: 'ds-section-accesses', @@ -71,50 +77,42 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { * @type {WorkspaceitemSectionAccessesObject} */ public accessesData: WorkspaceitemSectionAccessesObject; - - /** - * Array to track all subscriptions and unsubscribe them onDestroy - * @type {Array} - */ - protected subs: Subscription[] = []; - - /** * The collection name this submission belonging to * @type {string} */ public collectionName: string; - /** * Is the upload required * @type {boolean} */ public required$ = new BehaviorSubject(true); - + /** + * The form model + * @type {DynamicFormControlModel[]} + */ + formModel: DynamicFormControlModel[]; + /** + * Array to track all subscriptions and unsubscribe them onDestroy + * @type {Array} + */ + protected subs: Subscription[] = []; /** * The [[JsonPatchOperationPathCombiner]] object * @type {JsonPatchOperationPathCombiner} */ protected pathCombiner: JsonPatchOperationPathCombiner; - /** * A map representing all field prevous values * @type {Map} */ protected previousValue: Map = new Map(); - /** * A map representing all field on their way to be removed * @type {Map} */ protected fieldsOnTheirWayToBeRemoved: Map = new Map(); - /** - * The form model - * @type {DynamicFormControlModel[]} - */ - formModel: DynamicFormControlModel[]; - /** * Initialize instance variables * @@ -124,6 +122,7 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { * @param {JsonPatchOperationsBuilder} operationsBuilder * @param {SectionFormOperationsService} formOperationsService * @param {FormBuilderService} formBuilderService + * @param {TranslateService} translate * @param {SubmissionAccessesConfigService} accessesConfigService * @param {SectionAccessesService} accessesService * @param {SubmissionJsonPatchOperationsService} operationsService @@ -144,6 +143,129 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { super(undefined, injectedSectionData, injectedSubmissionId); } + /** + * Initialize form model values + * + * @param formModel + * The form model + */ + public initModelData(formModel: DynamicFormControlModel[]) { + this.accessesData.accessConditions.forEach((accessCondition, index) => { + Array.of('name', 'startDate', 'endDate') + .filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key])) + .forEach((key) => { + const metadataModel: any = this.formBuilderService.findById(key, formModel, index); + if (metadataModel) { + if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) { + const date = new Date(accessCondition[key]); + metadataModel.value = { + year: date.getUTCFullYear(), + month: date.getUTCMonth() + 1, + day: date.getUTCDate() + }; + } else { + metadataModel.value = accessCondition[key]; + } + } + }); + }); + } + + /** + * Method called when a form dfChange event is fired. + * Dispatch form operations based on changes. + */ + onChange(event: DynamicFormControlEvent) { + if (event.model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX) { + const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event); + const value = this.formOperationsService.getFieldValueFromChangeEvent(event); + this.operationsBuilder.replace(this.pathCombiner.getPath(path), value.value, true); + } else { + // validate form + this.formService.validateAllFormFields(this.formRef.formGroup); + this.formService.isValid(this.formId).pipe( + take(1), + filter((isValid) => isValid), + mergeMap(() => this.formService.getFormData(this.formId)), + take(1) + ).subscribe((formData: any) => { + const accessConditionsToSave = []; + formData.accessCondition + .map((accessConditions) => accessConditions.accessConditionGroup) + .filter((accessCondition) => isNotEmpty(accessCondition)) + .forEach((accessCondition) => { + let accessConditionOpt; + + this.availableAccessConditionOptions + .filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value) + .forEach((element) => accessConditionOpt = element); + + if (accessConditionOpt) { + const currentAccessCondition = Object.assign({}, accessCondition); + currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name); + + /* When start and end date fields are deactivated, their values may be still present in formData, + therefore it is necessary to delete them if they're not allowed by the current access condition option. */ + if (!accessConditionOpt.hasStartDate) { + delete currentAccessCondition.startDate; + } else if (accessCondition.startDate) { + const startDate = this.retrieveValueFromField(accessCondition.startDate); + currentAccessCondition.startDate = dateToISOFormat(startDate); + } + if (!accessConditionOpt.hasEndDate) { + delete currentAccessCondition.endDate; + } else if (accessCondition.endDate) { + const endDate = this.retrieveValueFromField(accessCondition.endDate); + currentAccessCondition.endDate = dateToISOFormat(endDate); + } + accessConditionsToSave.push(currentAccessCondition); + } + }); + + this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true); + }); + } + } + + /** + * 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)) { + return this.sectionData.data.hasOwnProperty(fieldId) && + isNotEmpty(this.sectionData.data[fieldId][index]) && + !this.isFieldToRemove(fieldId, index); + } else { + return false; + } + } + + /** + * Check if the specified field is on the way to be removed + * + * @param fieldId + * the section data retrieved from the serverù + * @param index + * the section data retrieved from the server + */ + isFieldToRemove(fieldId, index) { + return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index); + } + + /** + * Unsubscribe from all subscriptions + */ + onSectionDestroy() { + this.subs + .filter((subscription) => hasValue(subscription)) + .forEach((subscription) => subscription.unsubscribe()); + } + /** * Initialize all instance variables and retrieve collection default access conditions */ @@ -174,7 +296,6 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { * the section status */ protected getSectionStatus(): Observable { - console.log('Method not implemented.'); return of(true); } @@ -237,8 +358,12 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { const endDate = new DynamicDatePickerModel(endDateConfig, FORM_ACCESS_CONDITION_END_DATE_LAYOUT); const accessConditionGroupConfig = Object.assign({}, ACCESS_CONDITION_GROUP_CONFIG); accessConditionGroupConfig.group = [type]; - if (hasStart.length > 0) { accessConditionGroupConfig.group.push(startDate); } - if (hasEnd.length > 0) { accessConditionGroupConfig.group.push(endDate); } + if (hasStart.length > 0) { + accessConditionGroupConfig.group.push(startDate); + } + if (hasEnd.length > 0) { + accessConditionGroupConfig.group.push(endDate); + } return [new DynamicFormGroupModel(accessConditionGroupConfig, ACCESS_CONDITION_GROUP_LAYOUT)]; }; @@ -252,153 +377,8 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { return formModel; } - /** - * Initialize form model values - * - * @param formModel - * The form model - */ - public initModelData(formModel: DynamicFormControlModel[]) { - this.accessesData.accessConditions.forEach((accessCondition, index) => { - Array.of('name', 'startDate', 'endDate') - .filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key])) - .forEach((key) => { - const metadataModel: any = this.formBuilderService.findById(key, formModel, index); - if (metadataModel) { - if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) { - const date = new Date(accessCondition[key]); - metadataModel.value = { - year: date.getUTCFullYear(), - month: date.getUTCMonth() + 1, - day: date.getUTCDate() - }; - } else { - metadataModel.value = accessCondition[key]; - } - } - }); - }); - } - - onSubmit() { - // this.formService.validateAllFormFields(this.formRef.formGroup); - } - - /** - * Method called when a form dfChange event is fired. - * Dispatch form operations based on changes. - */ - onChange(event: DynamicFormControlEvent) { - if (event.model.type === 'CHECKBOX') { - const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event); - const value = this.formOperationsService.getFieldValueFromChangeEvent(event); - this.operationsBuilder.replace(this.pathCombiner.getPath(path), value.value, true); - } else { - // validate form - this.formService.validateAllFormFields(this.formRef.formGroup); - this.formService.isValid(this.formId).pipe( - take(1), - filter((isValid) => isValid), - mergeMap(() => this.formService.getFormData(this.formId)), - take(1), - mergeMap((formData: any) => { - console.log(formData); - const accessConditionsToSave = []; - formData.accessCondition - .map((accessConditions) => accessConditions.accessConditionGroup) - .filter((accessCondition) => isNotEmpty(accessCondition)) - .forEach((accessCondition) => { - let accessConditionOpt; - - this.availableAccessConditionOptions - .filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value) - .forEach((element) => accessConditionOpt = element); - - if (accessConditionOpt) { - const currentAccessCondition = Object.assign({}, accessCondition); - currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name); - - /* When start and end date fields are deactivated, their values may be still present in formData, - therefore it is necessary to delete them if they're not allowed by the current access condition option. */ - if (!accessConditionOpt.hasStartDate) { - delete currentAccessCondition.startDate; - } else if (accessCondition.startDate) { - const startDate = this.retrieveValueFromField(accessCondition.startDate); - currentAccessCondition.startDate = dateToISOFormat(startDate); - } - if (!accessConditionOpt.hasEndDate) { - delete currentAccessCondition.endDate; - } else if (accessCondition.endDate) { - const endDate = this.retrieveValueFromField(accessCondition.endDate); - currentAccessCondition.endDate = dateToISOFormat(endDate); - } - accessConditionsToSave.push(currentAccessCondition); - } - }); - - this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true); - - return this.formService.getFormData(this.formId); - - }) - ).subscribe((result: SubmissionObject[]) => { - console.log(result); - }); - } - } - protected retrieveValueFromField(field: any) { const temp = Array.isArray(field) ? field[0] : field; return (temp) ? temp.value : undefined; } - /** - * 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)) { - return this.sectionData.data.hasOwnProperty(fieldId) && - isNotEmpty(this.sectionData.data[fieldId][index]) && - !this.isFieldToRemove(fieldId, index); - } else { - return false; - } - } - - private hasRelatedCustomError(medatata): boolean { - const index = findIndex(this.sectionData.errorsToShow, { path: this.pathCombiner.getPath(medatata).path }); - if (index !== -1) { - const error = this.sectionData.errorsToShow[index]; - const validator = error.message.replace('error.validation.', ''); - return !environment.form.validatorMap.hasOwnProperty(validator); - } else { - return false; - } - } - - /** - * Check if the specified field is on the way to be removed - * - * @param fieldId - * the section data retrieved from the serverù - * @param index - * the section data retrieved from the server - */ - isFieldToRemove(fieldId, index) { - return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index); - } - - - /** - * Unsubscribe from all subscriptions - */ - onSectionDestroy() { - this.subs - .filter((subscription) => hasValue(subscription)) - .forEach((subscription) => subscription.unsubscribe()); - } } diff --git a/src/app/submission/sections/accesses/section-accesses.service.ts b/src/app/submission/sections/accesses/section-accesses.service.ts index 04867bbd4b0..3845f71871b 100644 --- a/src/app/submission/sections/accesses/section-accesses.service.ts +++ b/src/app/submission/sections/accesses/section-accesses.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; -import { Observable, of } from 'rxjs'; -import { distinctUntilChanged, filter, map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { distinctUntilChanged, filter } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { SubmissionState } from '../../submission.reducers'; import { isUndefined } from 'util'; -import { submissionSectionFromIdSelector, submissionSectionDataFromIdSelector } from 'src/app/submission/selectors'; +import { submissionSectionDataFromIdSelector } from '../../selectors'; /** @@ -30,8 +30,6 @@ export class SectionAccessesService { * The submission id * @param sectionId * The section id - * @param fileUUID - * The bitstream UUID * @returns {Observable} * Emits bitstream's metadata */ diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 218a91e0c4f..1c98d9012e0 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -37,9 +37,9 @@ import { ResearchEntitiesModule } from '../entity-groups/research-entities/resea import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component'; import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component'; import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component'; -import { SubmissionSectionAccessesComponent } from 'src/app/submission/sections/accesses/section-accesses.component'; -import { SubmissionAccessesConfigService } from 'src/app/core/config/submission-accesses-config.service'; -import { SectionAccessesService } from 'src/app/submission/sections/accesses/section-accesses.service'; +import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component'; +import { SubmissionAccessesConfigService } from '../core/config/submission-accesses-config.service'; +import { SectionAccessesService } from './sections/accesses/section-accesses.service'; const DECLARATIONS = [ SubmissionSectionUploadAccessConditionsComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index eb3daaad46c..e1c0fed2cac 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3801,6 +3801,8 @@ + "submission.sections.submit.progressbar.accessCondition": "Item access conditions", + "submission.sections.submit.progressbar.CClicense": "Creative commons license", "submission.sections.submit.progressbar.describe.recycle": "Recycle", @@ -3894,7 +3896,7 @@ "submission.sections.upload.upload-successful": "Upload successful", "submission.sections.accesses.form.discoverable-label": "Discoverable", - + "submission.submit.breadcrumbs": "New submission", From bb64058f63a83a0c3ae91f6baec6a37f50334275 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 23 Dec 2021 19:07:07 +0100 Subject: [PATCH 04/19] [CST-4506] fix test --- .../section-container.component.html | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/app/submission/sections/container/section-container.component.html b/src/app/submission/sections/container/section-container.component.html index d4b289a8544..c510c7ddf1d 100644 --- a/src/app/submission/sections/container/section-container.component.html +++ b/src/app/submission/sections/container/section-container.component.html @@ -1,36 +1,52 @@ -
- - + From 768c7f8b28722bc82514023be3b8170c2155f09b Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 13 Jan 2022 13:45:25 +0100 Subject: [PATCH 05/19] [CST-4505] Fixed canChangeDiscoverable managment and unit testing --- .../models/config-submission-access.model.ts | 3 + .../section-accesses-config.service.mock.ts | 43 ++++ .../section-accesses.component.spec.ts | 194 +++++++++++------- .../accesses/section-accesses.component.ts | 26 ++- 4 files changed, 187 insertions(+), 79 deletions(-) diff --git a/src/app/core/config/models/config-submission-access.model.ts b/src/app/core/config/models/config-submission-access.model.ts index a13a42a303f..9584fc6e3ce 100644 --- a/src/app/core/config/models/config-submission-access.model.ts +++ b/src/app/core/config/models/config-submission-access.model.ts @@ -19,6 +19,9 @@ export class SubmissionAccessModel extends ConfigObject { @autoserialize discoverable: boolean; + @autoserialize + canChangeDiscoverable: boolean; + @deserialize _links: { self: HALLink diff --git a/src/app/shared/mocks/section-accesses-config.service.mock.ts b/src/app/shared/mocks/section-accesses-config.service.mock.ts index 0362493035b..1ab9565acc8 100644 --- a/src/app/shared/mocks/section-accesses-config.service.mock.ts +++ b/src/app/shared/mocks/section-accesses-config.service.mock.ts @@ -37,8 +37,51 @@ const configRes = Object.assign(new SubmissionFormsModel(), { } }); + +const configResNotChangeDiscoverable = Object.assign(new SubmissionFormsModel(), { + 'id': 'AccessConditionDefaultConfiguration', + 'canChangeDiscoverable': false, + 'accessConditionOptions': [ + { + 'name': 'openaccess', + 'hasStartDate': false, + 'hasEndDate': false + }, + { + 'name': 'lease', + 'hasStartDate': false, + 'hasEndDate': true, + 'maxEndDate': '2022-06-20T12:17:44.420+00:00' + }, + { + 'name': 'embargo', + 'hasStartDate': true, + 'hasEndDate': false, + 'maxStartDate': '2024-12-20T12:17:44.420+00:00' + }, + { + 'name': 'administrator', + 'hasStartDate': false, + 'hasEndDate': false + } + ], + 'type': 'submissionaccessoption', + '_links': { + 'self': { + 'href': 'http://localhost:8080/server/api/config/submissionaccessoptions/AccessConditionDefaultConfiguration' + } + } +}); + export function getSubmissionAccessesConfigService(): SubmissionFormsConfigService { return jasmine.createSpyObj('SubmissionAccessesConfigService', { findByHref: createSuccessfulRemoteDataObject$(configRes), }); } + + +export function getSubmissionAccessesConfigNotChangeDiscoverableService(): SubmissionFormsConfigService { + return jasmine.createSpyObj('SubmissionAccessesConfigService', { + findByHref: createSuccessfulRemoteDataObject$(configResNotChangeDiscoverable), + }); +} diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts index 669d905ec29..4683283691c 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.spec.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -8,7 +8,7 @@ import { SectionsServiceStub } from '../../../shared/testing/sections-service.st import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock'; import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service'; -import { getSubmissionAccessesConfigService } from '../../../shared/mocks/section-accesses-config.service.mock'; +import { getSubmissionAccessesConfigService, getSubmissionAccessesConfigNotChangeDiscoverableService } from '../../../shared/mocks/section-accesses-config.service.mock'; import { SectionAccessesService } from './section-accesses.service'; import { SectionFormOperationsService } from '../form/section-form-operations.service'; import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; @@ -72,78 +72,130 @@ describe('SubmissionSectionAccessesComponent', () => { isValid: true }; - - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - BrowserModule, - TranslateModule.forRoot() - ], - declarations: [SubmissionSectionAccessesComponent, FormComponent], - providers: [ - { provide: SectionsService, useValue: sectionsServiceStub }, - { provide: FormBuilderService, useValue: builderService }, - { provide: SubmissionAccessesConfigService, useValue: submissionAccessesConfigService }, - { provide: SectionAccessesService, useValue: sectionAccessesService }, - { provide: SectionFormOperationsService, useValue: sectionFormOperationsService }, - { provide: JsonPatchOperationsBuilder, useValue: operationsBuilder }, - { provide: TranslateService, useValue: getMockTranslateService() }, - { provide: FormService, useValue: getMockFormService() }, - { provide: Store, useValue: storeStub }, - { provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub }, - { provide: 'sectionDataProvider', useValue: sectionData }, - { provide: 'submissionIdProvider', useValue: '1508' }, - ] - }) - .compileComponents(); - }); - - beforeEach(inject([Store], (store: Store) => { - fixture = TestBed.createComponent(SubmissionSectionAccessesComponent); - component = fixture.componentInstance; - formService = TestBed.inject(FormService); - formService.validateAllFormFields.and.callFake(() => null); - formService.isValid.and.returnValue(observableOf(true)); - formService.getFormData.and.returnValue(observableOf(mockAccessesFormData)); - fixture.detectChanges(); - })); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should have created formModel', () => { - expect(component.formModel).toBeTruthy(); - }); - - it('should have formModel length should be 2', () => { - expect(component.formModel.length).toEqual(2); - }); - - it('formModel should have 1 model type checkbox and 1 model type array', () => { - expect(component.formModel[0] instanceof DynamicCheckboxModel).toBeTrue(); - expect(component.formModel[1] instanceof DynamicFormArrayModel).toBeTrue(); + describe('First with canChangeDiscoverable true', () => { + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + BrowserModule, + TranslateModule.forRoot() + ], + declarations: [SubmissionSectionAccessesComponent, FormComponent], + providers: [ + { provide: SectionsService, useValue: sectionsServiceStub }, + { provide: FormBuilderService, useValue: builderService }, + { provide: SubmissionAccessesConfigService, useValue: submissionAccessesConfigService }, + { provide: SectionAccessesService, useValue: sectionAccessesService }, + { provide: SectionFormOperationsService, useValue: sectionFormOperationsService }, + { provide: JsonPatchOperationsBuilder, useValue: operationsBuilder }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: FormService, useValue: getMockFormService() }, + { provide: Store, useValue: storeStub }, + { provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub }, + { provide: 'sectionDataProvider', useValue: sectionData }, + { provide: 'submissionIdProvider', useValue: '1508' }, + ] + }) + .compileComponents(); + }); + + beforeEach(inject([Store], (store: Store) => { + fixture = TestBed.createComponent(SubmissionSectionAccessesComponent); + component = fixture.componentInstance; + formService = TestBed.inject(FormService); + formService.validateAllFormFields.and.callFake(() => null); + formService.isValid.and.returnValue(observableOf(true)); + formService.getFormData.and.returnValue(observableOf(mockAccessesFormData)); + fixture.detectChanges(); + })); + + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have created formModel', () => { + expect(component.formModel).toBeTruthy(); + }); + + it('should have formModel length should be 2', () => { + expect(component.formModel.length).toEqual(2); + }); + + it('formModel should have 1 model type checkbox and 1 model type array', () => { + expect(component.formModel[0] instanceof DynamicCheckboxModel).toBeTrue(); + expect(component.formModel[1] instanceof DynamicFormArrayModel).toBeTrue(); + }); + + it('formModel type array should have formgroup with 1 input and 2 datepickers', () => { + const formModel: any = component.formModel[1]; + const formGroup = formModel.groupFactory()[0].group; + expect(formGroup[0] instanceof DynamicSelectModel).toBeTrue(); + expect(formGroup[1] instanceof DynamicDatePickerModel).toBeTrue(); + expect(formGroup[2] instanceof DynamicDatePickerModel).toBeTrue(); + }); + + it('when checkbox changed it should call operationsBuilder replace function', () => { + component.onChange(checkboxChangeEvent); + fixture.detectChanges(); + + expect(operationsBuilder.replace).toHaveBeenCalled(); + }); + + it('when dropdown select changed it should call operationsBuilder add function', () => { + component.onChange(accessConditionChangeEvent); + fixture.detectChanges(); + expect(operationsBuilder.add).toHaveBeenCalled(); + }); }); - it('formModel type array should have formgroup with 1 input and 2 datepickers', () => { - const formModel: any = component.formModel[1]; - const formGroup = formModel.groupFactory()[0].group; - expect(formGroup[0] instanceof DynamicSelectModel).toBeTrue(); - expect(formGroup[1] instanceof DynamicDatePickerModel).toBeTrue(); - expect(formGroup[2] instanceof DynamicDatePickerModel).toBeTrue(); - }); - - it('when checkbox changed it should call operationsBuilder replace function', () => { - component.onChange(checkboxChangeEvent); - fixture.detectChanges(); - - expect(operationsBuilder.replace).toHaveBeenCalled(); - }); + describe('when canDescoverable is false', () => { + + + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + BrowserModule, + TranslateModule.forRoot() + ], + declarations: [SubmissionSectionAccessesComponent, FormComponent], + providers: [ + { provide: SectionsService, useValue: sectionsServiceStub }, + { provide: FormBuilderService, useValue: builderService }, + { provide: SubmissionAccessesConfigService, useValue: getSubmissionAccessesConfigNotChangeDiscoverableService() }, + { provide: SectionAccessesService, useValue: sectionAccessesService }, + { provide: SectionFormOperationsService, useValue: sectionFormOperationsService }, + { provide: JsonPatchOperationsBuilder, useValue: operationsBuilder }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: FormService, useValue: getMockFormService() }, + { provide: Store, useValue: storeStub }, + { provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub }, + { provide: 'sectionDataProvider', useValue: sectionData }, + { provide: 'submissionIdProvider', useValue: '1508' }, + ] + }) + .compileComponents(); + }); + + beforeEach(inject([Store], (store: Store) => { + fixture = TestBed.createComponent(SubmissionSectionAccessesComponent); + component = fixture.componentInstance; + formService = TestBed.inject(FormService); + formService.validateAllFormFields.and.callFake(() => null); + formService.isValid.and.returnValue(observableOf(true)); + formService.getFormData.and.returnValue(observableOf(mockAccessesFormData)); + fixture.detectChanges(); + })); + + + it('should have formModel length should be 1', () => { + expect(component.formModel.length).toEqual(1); + }); + + it('formModel should have only 1 model type array', () => { + expect(component.formModel[0] instanceof DynamicFormArrayModel).toBeTrue(); + }); - it('when dropdown select changed it should call operationsBuilder add function', () => { - component.onChange(accessConditionChangeEvent); - fixture.detectChanges(); - expect(operationsBuilder.add).toHaveBeenCalled(); }); }); diff --git a/src/app/submission/sections/accesses/section-accesses.component.ts b/src/app/submission/sections/accesses/section-accesses.component.ts index a1113501ee6..7f738e65a5e 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.ts @@ -113,6 +113,12 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { */ protected fieldsOnTheirWayToBeRemoved: Map = new Map(); + /** + * Defines if the access discoverable property can be managed + */ + public canChangeDiscoverable: boolean; + + /** * Initialize instance variables * @@ -282,6 +288,7 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { combineLatest(config$, accessData$).subscribe(([config, accessData]) => { this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : []; + this.canChangeDiscoverable = !!config.canChangeDiscoverable; this.accessesData = accessData; this.formModel = this.buildFileEditForm(); }); @@ -305,14 +312,17 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { protected buildFileEditForm() { const formModel: DynamicFormControlModel[] = []; - formModel.push( - new DynamicCheckboxModel({ - id: 'discoverable', - label: this.translate.instant('submission.sections.accesses.form.discoverable-label'), - name: 'discoverable', - value: this.accessesData.discoverable - }) - ); + if (this.canChangeDiscoverable) { + formModel.push( + new DynamicCheckboxModel({ + id: 'discoverable', + label: this.translate.instant('submission.sections.accesses.form.discoverable-label'), + name: 'discoverable', + value: this.accessesData.discoverable + }) + ); + } + const accessConditionTypeModelConfig = Object.assign({}, FORM_ACCESS_CONDITION_TYPE_CONFIG); const accessConditionsArrayConfig = Object.assign({}, ACCESS_CONDITIONS_FORM_ARRAY_CONFIG); From b81cc103ff5d44793dec889a294dbf867bd84006 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 20 Jan 2022 18:29:31 +0100 Subject: [PATCH 06/19] [CST-4506] Optimize submission sections components declarations in the submission.module --- src/app/submission/submission.module.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 1c98d9012e0..a7c938d074a 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -41,12 +41,18 @@ import { SubmissionSectionAccessesComponent } from './sections/accesses/section- import { SubmissionAccessesConfigService } from '../core/config/submission-accesses-config.service'; import { SectionAccessesService } from './sections/accesses/section-accesses.service'; -const DECLARATIONS = [ - SubmissionSectionUploadAccessConditionsComponent, +const ENTRY_COMPONENTS = [ + // put only entry components that use custom decorator SubmissionSectionUploadComponent, SubmissionSectionformComponent, SubmissionSectionLicenseComponent, SubmissionSectionCcLicensesComponent, + SubmissionSectionAccessesComponent +]; + +const DECLARATIONS = [ + ...ENTRY_COMPONENTS, + SubmissionSectionUploadAccessConditionsComponent, SectionsDirective, SubmissionEditComponent, ThemedSubmissionEditComponent, @@ -66,14 +72,6 @@ const DECLARATIONS = [ SubmissionImportExternalSearchbarComponent, SubmissionImportExternalPreviewComponent, SubmissionImportExternalCollectionComponent, - SubmissionSectionAccessesComponent -]; - -const ENTRY_COMPONENTS = [ - SubmissionSectionUploadComponent, - SubmissionSectionformComponent, - SubmissionSectionLicenseComponent, - SubmissionSectionCcLicensesComponent ]; @NgModule({ From 532d8fb9a31c25bdc6a299bdddd1bf93961be939 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 20 Jan 2022 18:30:29 +0100 Subject: [PATCH 07/19] [CST-4506] fix SubmissionSectionFormComponent name --- .../sections/form/section-form.component.spec.ts | 16 ++++++++-------- .../sections/form/section-form.component.ts | 2 +- src/app/submission/submission.module.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app/submission/sections/form/section-form.component.spec.ts b/src/app/submission/sections/form/section-form.component.spec.ts index 90861bce5eb..592691e6775 100644 --- a/src/app/submission/sections/form/section-form.component.spec.ts +++ b/src/app/submission/sections/form/section-form.component.spec.ts @@ -12,7 +12,7 @@ import { SubmissionServiceStub } from '../../../shared/testing/submission-servic import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock'; import { SectionsService } from '../sections.service'; import { SectionsServiceStub } from '../../../shared/testing/sections-service.stub'; -import { SubmissionSectionformComponent } from './section-form.component'; +import { SubmissionSectionFormComponent } from './section-form.component'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock'; import { getMockFormOperationsService } from '../../../shared/mocks/form-operations-service.mock'; @@ -137,11 +137,11 @@ const dynamicFormControlEvent: DynamicFormControlEvent = { type: DynamicFormControlEventType.Change }; -describe('SubmissionSectionformComponent test suite', () => { +describe('SubmissionSectionFormComponent test suite', () => { - let comp: SubmissionSectionformComponent; + let comp: SubmissionSectionFormComponent; let compAsAny: any; - let fixture: ComponentFixture; + let fixture: ComponentFixture; let submissionServiceStub: SubmissionServiceStub; let notificationsServiceStub: NotificationsServiceStub; let formService: any = getMockFormService(); @@ -167,7 +167,7 @@ describe('SubmissionSectionformComponent test suite', () => { ], declarations: [ FormComponent, - SubmissionSectionformComponent, + SubmissionSectionFormComponent, TestComponent ], providers: [ @@ -186,7 +186,7 @@ describe('SubmissionSectionformComponent test suite', () => { { provide: 'submissionIdProvider', useValue: submissionId }, { provide: SubmissionObjectDataService, useValue: { getHrefByID: () => observableOf('testUrl'), findById: () => createSuccessfulRemoteDataObject$(new WorkspaceItem()) } }, ChangeDetectorRef, - SubmissionSectionformComponent + SubmissionSectionFormComponent ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents().then(); @@ -215,7 +215,7 @@ describe('SubmissionSectionformComponent test suite', () => { testFixture.destroy(); }); - it('should create SubmissionSectionformComponent', inject([SubmissionSectionformComponent], (app: SubmissionSectionformComponent) => { + it('should create SubmissionSectionFormComponent', inject([SubmissionSectionFormComponent], (app: SubmissionSectionFormComponent) => { expect(app).toBeDefined(); @@ -224,7 +224,7 @@ describe('SubmissionSectionformComponent test suite', () => { describe('', () => { beforeEach(() => { - fixture = TestBed.createComponent(SubmissionSectionformComponent); + fixture = TestBed.createComponent(SubmissionSectionFormComponent); comp = fixture.componentInstance; compAsAny = comp; submissionServiceStub = TestBed.inject(SubmissionService as any); diff --git a/src/app/submission/sections/form/section-form.component.ts b/src/app/submission/sections/form/section-form.component.ts index 8ae246dcca0..9d9fe361de8 100644 --- a/src/app/submission/sections/form/section-form.component.ts +++ b/src/app/submission/sections/form/section-form.component.ts @@ -44,7 +44,7 @@ import { RemoteData } from '../../../core/data/remote-data'; templateUrl: './section-form.component.html', }) @renderSectionFor(SectionsType.SubmissionForm) -export class SubmissionSectionformComponent extends SectionModelComponent { +export class SubmissionSectionFormComponent extends SectionModelComponent { /** * The form id diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index a7c938d074a..4675a23d301 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CoreModule } from '../core/core.module'; import { SharedModule } from '../shared/shared.module'; -import { SubmissionSectionformComponent } from './sections/form/section-form.component'; +import { SubmissionSectionFormComponent } from './sections/form/section-form.component'; import { SectionsDirective } from './sections/sections.directive'; import { SectionsService } from './sections/sections.service'; import { SubmissionFormCollectionComponent } from './form/collection/submission-form-collection.component'; @@ -44,7 +44,7 @@ import { SectionAccessesService } from './sections/accesses/section-accesses.ser const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator SubmissionSectionUploadComponent, - SubmissionSectionformComponent, + SubmissionSectionFormComponent, SubmissionSectionLicenseComponent, SubmissionSectionCcLicensesComponent, SubmissionSectionAccessesComponent From 973ceb3b4b4a306caa9e8ecd8ad440e1036d9ebb Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 24 Jan 2022 19:54:21 +0100 Subject: [PATCH 08/19] [CST-4506] Address feedback --- ...onfig-accesses-conditions-options.model.ts | 5 +- .../models/config-submission-access.model.ts | 20 ++++- .../models/access-condition.model.ts | 25 ++++++ .../models/submission-accesses.model.ts | 37 -------- .../submission-accesses.resource-type.ts | 2 +- .../submission-item-access-condition.model.ts | 8 ++ ...sion-upload-file-access-condition.model.ts | 23 +---- .../workspaceitem-section-accesses.model.ts | 27 +++--- ...amic-form-control-container.component.html | 3 +- .../accesses/section-accesses.component.ts | 90 +++++-------------- .../accesses/section-accesses.model.ts | 21 +++-- .../accesses/section-accesses.service.ts | 12 +-- src/assets/i18n/en.json5 | 2 + 13 files changed, 115 insertions(+), 160 deletions(-) create mode 100644 src/app/core/submission/models/access-condition.model.ts delete mode 100644 src/app/core/submission/models/submission-accesses.model.ts create mode 100644 src/app/core/submission/models/submission-item-access-condition.model.ts diff --git a/src/app/core/config/models/config-accesses-conditions-options.model.ts b/src/app/core/config/models/config-accesses-conditions-options.model.ts index a12ae76b8e7..244b5019086 100644 --- a/src/app/core/config/models/config-accesses-conditions-options.model.ts +++ b/src/app/core/config/models/config-accesses-conditions-options.model.ts @@ -1,7 +1,5 @@ -import { autoserialize } from 'cerialize'; - /** - * Model class for an Access Condition + * Model class for an Item Access Condition */ export class AccessesConditionOption { @@ -15,7 +13,6 @@ export class AccessesConditionOption { */ groupName: string; - /** * A boolean representing if this Access Condition has a start date */ diff --git a/src/app/core/config/models/config-submission-access.model.ts b/src/app/core/config/models/config-submission-access.model.ts index 9584fc6e3ce..7db96acf2bd 100644 --- a/src/app/core/config/models/config-submission-access.model.ts +++ b/src/app/core/config/models/config-submission-access.model.ts @@ -1,27 +1,39 @@ -import { autoserialize, inheritSerialization, deserialize } from 'cerialize'; -import { typedObject, link } from '../../cache/builders/build-decorators'; +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; +import { typedObject } from '../../cache/builders/build-decorators'; import { ConfigObject } from './config.model'; import { AccessesConditionOption } from './config-accesses-conditions-options.model'; import { SUBMISSION_ACCESSES_TYPE } from './config-type'; import { HALLink } from '../../shared/hal-link.model'; - +/** + * Class for the configuration describing the item accesses condition + */ @typedObject @inheritSerialization(ConfigObject) export class SubmissionAccessModel extends ConfigObject { static type = SUBMISSION_ACCESSES_TYPE; + /** - * A list of available bitstream access conditions + * A list of available item access conditions */ @autoserialize accessConditionOptions: AccessesConditionOption[]; + /** + * Boolean that indicates whether the current item must be findable via search or browse. + */ @autoserialize discoverable: boolean; + /** + * Boolean that indicates whether or not the user can change the discoverable flag. + */ @autoserialize canChangeDiscoverable: boolean; + /** + * The links to all related resources returned by the rest api. + */ @deserialize _links: { self: HALLink diff --git a/src/app/core/submission/models/access-condition.model.ts b/src/app/core/submission/models/access-condition.model.ts new file mode 100644 index 00000000000..5ec5c2c4340 --- /dev/null +++ b/src/app/core/submission/models/access-condition.model.ts @@ -0,0 +1,25 @@ +/** + * An interface to represent an access condition. + */ +export class AccessConditionObject { + + /** + * The access condition id + */ + id: string; + + /** + * The access condition name + */ + name: string; + + /** + * Possible start date of the access condition + */ + startDate: string; + + /** + * Possible end date of the access condition + */ + endDate: string; +} diff --git a/src/app/core/submission/models/submission-accesses.model.ts b/src/app/core/submission/models/submission-accesses.model.ts deleted file mode 100644 index afad90b2ff4..00000000000 --- a/src/app/core/submission/models/submission-accesses.model.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { autoserialize, inheritSerialization } from 'cerialize'; -import { typedObject } from '../../cache/builders/build-decorators'; -import { excludeFromEquals } from '../../utilities/equals.decorators'; -import { ResourceType } from '../../shared/resource-type'; -import { HALResource } from '../../shared/hal-resource.model'; -import { SUBMISSION_ACCESSES } from './submission-accesses.resource-type'; - -@typedObject -@inheritSerialization(HALResource) -export class SubmissionAccesses extends HALResource { - - static type = SUBMISSION_ACCESSES; - - /** - * The object type - */ - @excludeFromEquals - @autoserialize - type: ResourceType; - - @autoserialize - discoverable: boolean; - - @autoserialize - accessConditions: AccessConditions[]; -} - -export interface AccessConditions { - name: string; - startDate?: Date; - hasStartDate?: boolean; - maxStartDate?: string; - hasEndDate?: boolean; - maxEndDate?: string; - endDate?: Date; -} - diff --git a/src/app/core/submission/models/submission-accesses.resource-type.ts b/src/app/core/submission/models/submission-accesses.resource-type.ts index 0aacb895223..0634a633373 100644 --- a/src/app/core/submission/models/submission-accesses.resource-type.ts +++ b/src/app/core/submission/models/submission-accesses.resource-type.ts @@ -1,7 +1,7 @@ import { ResourceType } from '../../shared/resource-type'; /** - * The resource type for License + * The resource type for Accesses section * * Needs to be in a separate file to prevent circular * dependencies in webpack. diff --git a/src/app/core/submission/models/submission-item-access-condition.model.ts b/src/app/core/submission/models/submission-item-access-condition.model.ts new file mode 100644 index 00000000000..60e749b7865 --- /dev/null +++ b/src/app/core/submission/models/submission-item-access-condition.model.ts @@ -0,0 +1,8 @@ +import { AccessConditionObject } from './access-condition.model'; + +/** + * An interface to represent item's access condition. + */ +export class SubmissionItemAccessConditionObject extends AccessConditionObject { + +} diff --git a/src/app/core/submission/models/submission-upload-file-access-condition.model.ts b/src/app/core/submission/models/submission-upload-file-access-condition.model.ts index fa4d9b90620..7be9d6f1f72 100644 --- a/src/app/core/submission/models/submission-upload-file-access-condition.model.ts +++ b/src/app/core/submission/models/submission-upload-file-access-condition.model.ts @@ -1,25 +1,8 @@ +import { AccessConditionObject } from './access-condition.model'; + /** * An interface to represent bitstream's access condition. */ -export class SubmissionUploadFileAccessConditionObject { - - /** - * The access condition id - */ - id: string; - - /** - * The access condition name - */ - name: string; - - /** - * Possible start date of the access condition - */ - startDate: string; +export class SubmissionUploadFileAccessConditionObject extends AccessConditionObject { - /** - * Possible end date of the access condition - */ - endDate: string; } diff --git a/src/app/core/submission/models/workspaceitem-section-accesses.model.ts b/src/app/core/submission/models/workspaceitem-section-accesses.model.ts index c464f858cfa..2d571f67f8e 100644 --- a/src/app/core/submission/models/workspaceitem-section-accesses.model.ts +++ b/src/app/core/submission/models/workspaceitem-section-accesses.model.ts @@ -1,18 +1,21 @@ +import { SubmissionItemAccessConditionObject } from './submission-item-access-condition.model'; + /** - * An interface to represent the submission's creative commons license section data. + * An interface to represent the submission's item accesses condition. */ export interface WorkspaceitemSectionAccessesObject { + /** + * The access condition id + */ id: string; + + /** + * Boolean that indicates whether the current item must be findable via search or browse. + */ discoverable: boolean; - accessConditions: [ - { - name: string; - startDate?: Date; - hasStartDate?: boolean; - maxStartDate?: string; - hasEndDate?: boolean; - maxEndDate?: string; - endDate?: Date; - } - ]; + + /** + * A list of available item access conditions + */ + accessConditions: SubmissionItemAccessConditionObject[]; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html index fc115e043a9..55e354ea7ab 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html @@ -14,7 +14,8 @@
-
Date: Tue, 25 Jan 2022 10:40:11 +0100 Subject: [PATCH 09/19] [CST-4506] Fix issue with submission bitstream edit --- .../file/section-upload-file.component.ts | 4 +-- src/app/submission/submission.module.ts | 32 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/app/submission/sections/upload/file/section-upload-file.component.ts b/src/app/submission/sections/upload/file/section-upload-file.component.ts index 9fd005a8527..a8e05fcf40d 100644 --- a/src/app/submission/sections/upload/file/section-upload-file.component.ts +++ b/src/app/submission/sections/upload/file/section-upload-file.component.ts @@ -176,8 +176,8 @@ export class SubmissionSectionUploadFileComponent implements OnChanges, OnInit { .getFileData(this.submissionId, this.sectionId, this.fileId).pipe( filter((bitstream) => isNotUndefined(bitstream))) .subscribe((bitstream) => { - this.fileData = bitstream; - } + this.fileData = bitstream; + } ) ); } diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 93ca85d0e00..939d1bff292 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -38,7 +38,7 @@ import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.com import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component'; import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component'; import { FormModule } from '../shared/form/form.module'; -import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbAccordionModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component'; import { SubmissionAccessesConfigService } from '../core/config/submission-accesses-config.service'; import { SectionAccessesService } from './sections/accesses/section-accesses.service'; @@ -49,12 +49,12 @@ const ENTRY_COMPONENTS = [ SubmissionSectionFormComponent, SubmissionSectionLicenseComponent, SubmissionSectionCcLicensesComponent, - SubmissionSectionAccessesComponent + SubmissionSectionAccessesComponent, + SubmissionSectionUploadFileEditComponent ]; const DECLARATIONS = [ ...ENTRY_COMPONENTS, - SubmissionSectionUploadAccessConditionsComponent, SectionsDirective, SubmissionEditComponent, ThemedSubmissionEditComponent, @@ -66,6 +66,7 @@ const DECLARATIONS = [ ThemedSubmissionSubmitComponent, SubmissionUploadFilesComponent, SubmissionSectionContainerComponent, + SubmissionSectionUploadAccessConditionsComponent, SubmissionSectionUploadFileComponent, SubmissionSectionUploadFileEditComponent, SubmissionSectionUploadFileViewComponent, @@ -77,17 +78,18 @@ const DECLARATIONS = [ ]; @NgModule({ - imports: [ - CommonModule, - CoreModule.forRoot(), - SharedModule, - StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig), - EffectsModule.forFeature(submissionEffects), - JournalEntitiesModule.withEntryComponents(), - ResearchEntitiesModule.withEntryComponents(), - FormModule, - NgbAccordionModule - ], + imports: [ + CommonModule, + CoreModule.forRoot(), + SharedModule, + StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig), + EffectsModule.forFeature(submissionEffects), + JournalEntitiesModule.withEntryComponents(), + ResearchEntitiesModule.withEntryComponents(), + FormModule, + NgbAccordionModule, + NgbModalModule + ], declarations: DECLARATIONS, exports: DECLARATIONS, providers: [ @@ -110,7 +112,7 @@ export class SubmissionModule { static withEntryComponents() { return { ngModule: SubmissionModule, - providers: ENTRY_COMPONENTS.map((component) => ({provide: component})) + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) }; } } From db3d760f2b1305027faff81f3f7304c5fc2ea76d Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 25 Jan 2022 10:40:53 +0100 Subject: [PATCH 10/19] [CST-4506] Fix LGTM issues --- .../submission-accesses-config.service.ts | 2 -- .../section-accesses.component.spec.ts | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/app/core/config/submission-accesses-config.service.ts b/src/app/core/config/submission-accesses-config.service.ts index ace3fd2316e..de9afc66eab 100644 --- a/src/app/core/config/submission-accesses-config.service.ts +++ b/src/app/core/config/submission-accesses-config.service.ts @@ -16,8 +16,6 @@ import { SubmissionAccessesModel } from './models/config-submission-accesses.mod import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; -import { map, switchMap } from 'rxjs/operators'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; /** * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts index 4683283691c..5509cab4bb8 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.spec.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -8,31 +8,34 @@ import { SectionsServiceStub } from '../../../shared/testing/sections-service.st import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock'; import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service'; -import { getSubmissionAccessesConfigService, getSubmissionAccessesConfigNotChangeDiscoverableService } from '../../../shared/mocks/section-accesses-config.service.mock'; +import { + getSubmissionAccessesConfigNotChangeDiscoverableService, + getSubmissionAccessesConfigService +} from '../../../shared/mocks/section-accesses-config.service.mock'; import { SectionAccessesService } from './section-accesses.service'; import { SectionFormOperationsService } from '../form/section-form-operations.service'; import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service'; import { getSectionAccessesService } from '../../../shared/mocks/section-accesses.service.mock'; -import { getMockFormOperationsService } from 'src/app/shared/mocks/form-operations-service.mock'; -import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; -import { SubmissionJsonPatchOperationsServiceStub } from 'src/app/shared/testing/submission-json-patch-operations-service.stub'; +import { getMockFormOperationsService } from '../../../shared/mocks/form-operations-service.mock'; +import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock'; +import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testing/submission-json-patch-operations-service.stub'; import { BrowserModule } from '@angular/platform-browser'; import { of as observableOf } from 'rxjs'; import { Store } from '@ngrx/store'; -import { FormComponent } from 'src/app/shared/form/form.component'; +import { FormComponent } from '../../../shared/form/form.component'; import { DynamicCheckboxModel, DynamicDatePickerModel, DynamicFormArrayModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; -import { AppState } from 'src/app/app.reducer'; -import { getMockFormService } from 'src/app/shared/mocks/form-service.mock'; -import { mockAccessesFormData } from 'src/app/shared/mocks/submission.mock'; -import { accessConditionChangeEvent, checkboxChangeEvent } from 'src/app/shared/testing/form-event.stub'; +import { AppState } from '../../../app.reducer'; +import { getMockFormService } from '../../../shared/mocks/form-service.mock'; +import { mockAccessesFormData } from '../../../shared/mocks/submission.mock'; +import { accessConditionChangeEvent, checkboxChangeEvent } from '../../../shared/testing/form-event.stub'; describe('SubmissionSectionAccessesComponent', () => { let component: SubmissionSectionAccessesComponent; From 42cc68f8b7361a33cd558809e90e398cd5eeeaa2 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 27 Jan 2022 15:47:25 +0100 Subject: [PATCH 11/19] [CST-4506] remove unused mock event properties --- src/app/shared/testing/form-event.stub.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/app/shared/testing/form-event.stub.ts b/src/app/shared/testing/form-event.stub.ts index a2b1f180310..30436a82fca 100644 --- a/src/app/shared/testing/form-event.stub.ts +++ b/src/app/shared/testing/form-event.stub.ts @@ -1,4 +1,4 @@ -import { FormControl, FormGroup, AbstractControl } from '@angular/forms'; +import { FormControl, FormGroup } from '@angular/forms'; import { DynamicCheckboxModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; export const accessConditionChangeEvent = { @@ -11,10 +11,7 @@ export const accessConditionChangeEvent = { defaultPrevented: false, eventPhase: 0, isTrusted: true, - path: ['input#accessCondition-0-endDate.form-control.ng-touched.ng-dirty.ng-valid', 'div.input-group.ng-touched.ng-valid.ng-pristine', 'ds-dynamic-date-picker-inline.ng-star-inserted, div, div, div, div.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-control-container.col-6.ng-star-inserted, div#accessCondition-0-accessConditionGroup.form-row.ng-star-inserted.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-group.ng-star-inserted, div, div, div, div.pl-1.pr-1.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-control-container.form-group.flex-fill.access-condition-group.ng-star-inserted.ng-to…, div.cdk-drag.cdk-drag-handle.form-row.cdk-drag-disabled.ng-star-inserted.ng-touched.ng-valid.ng-pris…, div#cdk-drop-list-5.cdk-drop-list, div#accessCondition.ng-star-inserted.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-array.ng-star-inserted, div, div, div, div.form-group.ng-touched.ng-valid.ng-pristine, ds-dynamic-form-control-container.ng-star-inserted, ds-dynamic-form.ng-touched.ng-valid.ng-pristine, form.form-horizontal.ng-touched.ng-valid.ng-pristine, div.container-fluid, ds-form.ng-star-inserted, ds-section-accesses.ng-star-inserted, div#sectionContent_AccessConditionDefaultConfiguration.ng-star-inserted, div.card-body, div#AccessConditionDefaultConfiguration.collapse.show.ng-star-inserted, div.card.ng-star-inserted, ngb-accordion.accordion, div#section_AccessConditionDefaultConfiguration.section-focus, ds-submission-section-container.ng-star-inserted, div.submission-form-content, div.container-fluid, ds-submission-form, div.submission-submit-container, ds-submission-edit.ng-star-inserted, ds-themed-submission-edit.ng-star-inserted, div.ng-tns-c392-0, main.main-content.ng-tns-c392-0, div.inner-wrapper.ng-tns-c392-0.ng-trigger.ng-trigger-slideSidebarPadding, div.outer-wrapper.ng-tns-c392-0.ng-star-inserted, ds-root.ng-tns-c392-0.ng-star-inserted, ds-themed-root, ds-app, body, html.wf-droidsans-n4-active.wf-active, document, Window'], returnValue: true, - srcElement: 'input#accessCondition-0-endDate.form-control.ng-touched.ng-dirty.ng-valid', - target: 'input#accessCondition-0-endDate.form-control.ng-touched.ng-dirty.ng-valid', timeStamp: 143042.8999999999, type: 'change', }, @@ -64,10 +61,7 @@ export const checkboxChangeEvent = { defaultPrevented: false, eventPhase: 0, isTrusted: true, - path: ['input#discoverable.form-check-input.ng-valid.ng-touched.ng-pristine', 'label.form-check-label', 'div.form-check.ng-touched.ng-valid.ng-pristine', 'dynamic-ng-bootstrap-checkbox.ng-star-inserted', 'div', 'div', 'div', 'div.form-group.ng-touched.ng-valid.ng-pristine', 'ds-dynamic-form-control-container.ng-star-inserted', 'ds-dynamic-form.ng-touched.ng-valid.ng-pristine', 'form.form-horizontal.ng-touched.ng-valid.ng-pristine', 'div.container-fluid', 'ds-form.ng-star-inserted', 'ds-section-accesses.ng-star-inserted', 'div#sectionContent_AccessConditionDefaultConfiguration.ng-star-inserted', 'div.card-body', 'div#AccessConditionDefaultConfiguration.collapse.show.ng-star-inserted', 'div.card.ng-star-inserted', 'ngb-accordion.accordion', 'div#section_AccessConditionDefaultConfiguration.section-focus', 'ds-submission-section-container.ng-star-inserted', 'div.submission-form-content', 'div.container-fluid', 'ds-submission-form', 'div.submission-submit-container', 'ds-submission-edit.ng-star-inserted', 'ds-themed-submission-edit.ng-star-inserted', 'div.ng-tns-c392-0', 'main.main-content.ng-tns-c392-0', 'div.inner-wrapper.ng-tns-c392-0.ng-trigger.ng-trigger-slideSidebarPadding', 'div.outer-wrapper.ng-tns-c392-0.ng-star-inserted', 'ds-root.ng-tns-c392-0.ng-star-inserted', 'ds-themed-root', 'ds-app', 'body', 'html.wf-droidsans-n4-active.wf-active', 'document', 'Window'], returnValue: true, - srcElement: 'input#discoverable.form-check-input.ng-valid.ng-touched.ng-pristine', - target: 'input#discoverable.form-check-input.ng-valid.ng-touched.ng-pristine', timeStamp: 143042.8999999999, type: 'change', }, From 8a668a5073db55f5ffcb248c8c1dc3b0e6f0688c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 27 Jan 2022 18:58:28 +0100 Subject: [PATCH 12/19] [CST-4506] fix issue with delete button alignment --- .../sections/accesses/section-accesses.component.scss | 5 +++++ .../submission/sections/accesses/section-accesses.model.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/submission/sections/accesses/section-accesses.component.scss b/src/app/submission/sections/accesses/section-accesses.component.scss index e69de29bb2d..cb9273e85b5 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.scss +++ b/src/app/submission/sections/accesses/section-accesses.component.scss @@ -0,0 +1,5 @@ +::ng-deep .access-condition-group { + position: relative; + top: -2.3rem; + margin-bottom: -2.3rem; +} diff --git a/src/app/submission/sections/accesses/section-accesses.model.ts b/src/app/submission/sections/accesses/section-accesses.model.ts index 434ee047416..db66b488b29 100644 --- a/src/app/submission/sections/accesses/section-accesses.model.ts +++ b/src/app/submission/sections/accesses/section-accesses.model.ts @@ -31,7 +31,7 @@ export const ACCESS_CONDITION_GROUP_CONFIG: DynamicFormGroupModelConfig = { export const ACCESS_CONDITION_GROUP_LAYOUT: DynamicFormControlLayout = { element: { - host: 'form-group flex-fill', + host: 'form-group flex-fill access-condition-group', container: 'pl-1 pr-1', control: 'form-row ' } @@ -43,7 +43,7 @@ export const ACCESS_CONDITIONS_FORM_ARRAY_CONFIG: DynamicFormArrayModelConfig = }; export const ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT: DynamicFormControlLayout = { grid: { - group: 'form-row', + group: 'form-row pt-4', } }; From 9019b809939868a48d59936ad35b96ab15fcc685 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 27 Jan 2022 19:00:11 +0100 Subject: [PATCH 13/19] [CST-4506] Disable drag and drop for access conditions form group --- .../array-group/dynamic-form-array.component.html | 4 ++-- .../array-group/dynamic-form-array.component.scss | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html index 2f91c0d1adb..bc41ade088a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html @@ -15,8 +15,8 @@ [cdkDragDisabled]="dragDisabled" [cdkDragPreviewClass]="'ds-submission-reorder-dragging'"> -
- +
+