From deef684d709ae57dd108da16370d3093dff299bb Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 25 Jun 2019 13:25:26 +0200 Subject: [PATCH 001/187] Added response mocking service --- src/app/core/core.module.ts | 19 ++- .../dspace-rest-v2/dspace-rest-v2.service.ts | 2 +- .../endpoint-mocking-rest.service.spec.ts | 75 ++++++++++++ .../endpoint-mocking-rest.service.ts | 108 ++++++++++++++++++ .../dspace-rest-v2/mocks/mock-response-map.ts | 10 ++ webpack/webpack.common.js | 4 + 6 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.spec.ts create mode 100644 src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts create mode 100644 src/app/core/dspace-rest-v2/mocks/mock-response-map.ts diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 6550435aa3..5ff9a0336e 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -54,7 +54,7 @@ import { UUIDService } from './shared/uuid.service'; import { AuthenticatedGuard } from './auth/authenticated.guard'; import { AuthRequestService } from './auth/auth-request.service'; import { AuthResponseParsingService } from './auth/auth-response-parsing.service'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; import { AuthInterceptor } from './auth/auth.interceptor'; import { HALEndpointService } from './shared/hal-endpoint.service'; import { FacetValueResponseParsingService } from './data/facet-value-response-parsing.service'; @@ -87,6 +87,21 @@ import { MyDSpaceResponseParsingService } from './data/mydspace-response-parsing import { ClaimedTaskDataService } from './tasks/claimed-task-data.service'; import { PoolTaskDataService } from './tasks/pool-task-data.service'; import { TaskResponseParsingService } from './tasks/task-response-parsing.service'; +import { + MOCK_RESPONSE_MAP, + MockResponseMap, + mockResponseMap +} from './dspace-rest-v2/mocks/mock-response-map'; +import { EndpointMockingRestService } from './dspace-rest-v2/endpoint-mocking-rest.service'; +import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../../config'; + +export const restServiceFactory = (cfg: GlobalConfig, mocks: MockResponseMap, http: HttpClient) => { + if (ENV_CONFIG.production) { + return new DSpaceRESTv2Service(http); + } else { + return new EndpointMockingRestService(cfg, mocks, http); + } +}; const IMPORTS = [ CommonModule, @@ -110,6 +125,8 @@ const PROVIDERS = [ CommunityDataService, CollectionDataService, DSOResponseParsingService, + { provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap }, + { provide: DSpaceRESTv2Service, useFactory: restServiceFactory, deps: [GLOBAL_CONFIG, MOCK_RESPONSE_MAP, HttpClient]}, DSpaceRESTv2Service, DynamicFormLayoutService, DynamicFormService, diff --git a/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts b/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts index 290f4be8a2..cf9b1067c1 100644 --- a/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts +++ b/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts @@ -26,7 +26,7 @@ export interface HttpOptions { @Injectable() export class DSpaceRESTv2Service { - constructor(private http: HttpClient) { + constructor(protected http: HttpClient) { } diff --git a/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.spec.ts b/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.spec.ts new file mode 100644 index 0000000000..a53762e8ce --- /dev/null +++ b/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.spec.ts @@ -0,0 +1,75 @@ +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { of as observableOf } from 'rxjs'; +import { GlobalConfig } from '../../../config/global-config.interface'; +import { RestRequestMethod } from '../data/rest-request-method'; +import { EndpointMockingRestService } from './endpoint-mocking-rest.service'; +import { MockResponseMap } from './mocks/mock-response-map'; + +describe('EndpointMockingRestService', () => { + let service: EndpointMockingRestService; + + const serverHttpResponse: HttpResponse = { + body: { bar: false }, + headers: new HttpHeaders(), + statusText: '200' + } as HttpResponse; + + const mockResponseMap: MockResponseMap = new Map([ + [ '/foo', { bar: true } ] + ]); + + beforeEach(() => { + const EnvConfig = { + rest: { + nameSpace: '/api' + } + } as GlobalConfig; + + const httpStub = jasmine.createSpyObj('http', { + get: observableOf(serverHttpResponse), + request: observableOf(serverHttpResponse) + }); + + service = new EndpointMockingRestService(EnvConfig, mockResponseMap, httpStub); + }); + + describe('get', () => { + describe('when the URL is mocked', () => { + it('should return the mock data', (done) => { + service.get('https://rest.com/api/foo').subscribe((response) => { + expect(response.payload).toEqual({ bar: true }); + done(); + }); + }); + }); + + describe('when the URL isn\'t mocked', () => { + it('should return the server data', (done) => { + service.get('https://rest.com/api').subscribe((response) => { + expect(response.payload).toEqual({ bar: false }); + done(); + }); + }); + }); + }); + + describe('request', () => { + describe('when the URL is mocked', () => { + it('should return the mock data', (done) => { + service.request(RestRequestMethod.GET, 'https://rest.com/api/foo').subscribe((response) => { + expect(response.payload).toEqual({ bar: true }); + done(); + }); + }); + }); + + describe('when the URL isn\'t mocked', () => { + it('should return the server data', (done) => { + service.request(RestRequestMethod.GET, 'https://rest.com/api').subscribe((response) => { + expect(response.payload).toEqual({ bar: false }); + done(); + }); + }); + }); + }); +}); diff --git a/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts b/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts new file mode 100644 index 0000000000..91ce479f40 --- /dev/null +++ b/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts @@ -0,0 +1,108 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http' +import { Inject, Injectable } from '@angular/core'; +import { Observable, of as observableOf } from 'rxjs'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { isEmpty } from '../../shared/empty.util'; +import { RestRequestMethod } from '../data/rest-request-method'; + +import { DSpaceRESTV2Response } from './dspace-rest-v2-response.model'; +import { DSpaceRESTv2Service, HttpOptions } from './dspace-rest-v2.service'; +import { MOCK_RESPONSE_MAP, MockResponseMap } from './mocks/mock-response-map'; + +/** + * Service to access DSpace's REST API. + * + * If a URL is found in this.mockResponseMap, it returns the mock response instead + */ +@Injectable() +export class EndpointMockingRestService extends DSpaceRESTv2Service { + + constructor( + @Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, + @Inject(MOCK_RESPONSE_MAP) protected mockResponseMap: MockResponseMap, + protected http: HttpClient + ) { + super(http); + } + + /** + * Performs a request to the REST API with the `get` http method. + * + * If the URL is found in this.mockResponseMap, + * it returns the mock response instead + * + * @param absoluteURL + * A URL + * @return Observable + * An Observable containing the response + */ + get(absoluteURL: string): Observable { + const mockData = this.getMockData(absoluteURL); + if (isEmpty(mockData)) { + return super.get(absoluteURL); + } else { + return this.toMockResponse$(mockData); + } + } + + /** + * Performs a request to the REST API. + * + * If the URL is found in this.mockResponseMap, + * it returns the mock response instead + * + * @param method + * the HTTP method for the request + * @param url + * the URL for the request + * @param body + * an optional body for the request + * @return Observable + * An Observable containing the response from the server + */ + request(method: RestRequestMethod, url: string, body?: any, options?: HttpOptions): Observable { + const mockData = this.getMockData(url); + if (isEmpty(mockData)) { + return super.request(method, url, body, options); + } else { + return this.toMockResponse$(mockData); + } + } + + /** + * Turn the mock object in to an Observable + * + * @param mockData + * the mock response + * @return + * an Observable containing the mock response + */ + private toMockResponse$(mockData: any): Observable { + return observableOf({ + payload: mockData, + headers: new HttpHeaders(), + statusCode: 200, + statusText: 'OK' + }); + } + + /** + * Get the mock response associated with this URL from this.mockResponseMap + * + * @param urlStr + * the URL to fetch a mock reponse for + * @return any + * the mock response if there is one, undefined otherwise + */ + private getMockData(urlStr: string): any { + const url = new URL(urlStr); + const key = url.pathname.slice(this.EnvConfig.rest.nameSpace.length); + if (this.mockResponseMap.has(key)) { + // parse and stringify to clone the object to ensure that any changes made + // to it afterwards don't affect future calls + return JSON.parse(JSON.stringify(this.mockResponseMap.get(key))); + } else { + return undefined; + } + } +} diff --git a/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts b/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts new file mode 100644 index 0000000000..9a1bf4239b --- /dev/null +++ b/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts @@ -0,0 +1,10 @@ +import { InjectionToken } from '@angular/core'; +import mockSuggestResponse from './mock-suggest-response.json'; + +export class MockResponseMap extends Map {}; + +export const MOCK_RESPONSE_MAP: InjectionToken = new InjectionToken('mockResponseMap'); + +export const mockResponseMap: MockResponseMap = new Map([ + [ '/discover/suggestions', mockSuggestResponse ] +]); diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 7fb4656a15..723e883462 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -91,6 +91,10 @@ module.exports = { { test: /\.html$/, loader: 'raw-loader' + }, + { + test: /\.json$/, + loader: 'json-loader' } ] }, From 0746aaed43f06592121b6d2b0e7d49a736e2c541 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 25 Jun 2019 16:35:26 +0200 Subject: [PATCH 002/187] added JSON response, created new submission section --- package.json | 1 + src/app/core/core.module.ts | 2 +- .../endpoint-mocking-rest.service.ts | 1 + .../dspace-rest-v2/mocks/mock-response-map.ts | 4 +- .../mocks/mock-submission-response.json | 377 ++++++++++++++++++ .../section-relationships.component.html | 49 +++ .../section-relationships.component.scss | 0 .../section-relationships.component.ts | 49 +++ src/app/submission/sections/sections-type.ts | 1 + src/app/submission/submission.service.ts | 14 +- .../submit/submission-submit.component.ts | 6 +- yarn.lock | 5 + 12 files changed, 497 insertions(+), 12 deletions(-) create mode 100644 src/app/core/dspace-rest-v2/mocks/mock-submission-response.json create mode 100644 src/app/submission/sections/relationships/section-relationships.component.html create mode 100644 src/app/submission/sections/relationships/section-relationships.component.scss create mode 100644 src/app/submission/sections/relationships/section-relationships.component.ts diff --git a/package.json b/package.json index 7755f3d3d0..ae4b37d6fc 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,7 @@ "https": "1.0.0", "js-cookie": "2.2.0", "js.clone": "0.0.3", + "json-loader": "^0.5.7", "jsonschema": "1.2.2", "jwt-decode": "^2.2.0", "methods": "1.1.2", diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 5ff9a0336e..7e99d842b7 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -96,6 +96,7 @@ import { EndpointMockingRestService } from './dspace-rest-v2/endpoint-mocking-re import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../../config'; export const restServiceFactory = (cfg: GlobalConfig, mocks: MockResponseMap, http: HttpClient) => { + console.log('REST SERVICE FACTORY'); if (ENV_CONFIG.production) { return new DSpaceRESTv2Service(http); } else { @@ -127,7 +128,6 @@ const PROVIDERS = [ DSOResponseParsingService, { provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap }, { provide: DSpaceRESTv2Service, useFactory: restServiceFactory, deps: [GLOBAL_CONFIG, MOCK_RESPONSE_MAP, HttpClient]}, - DSpaceRESTv2Service, DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService, diff --git a/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts b/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts index 91ce479f40..400a5fe7ba 100644 --- a/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts +++ b/src/app/core/dspace-rest-v2/endpoint-mocking-rest.service.ts @@ -39,6 +39,7 @@ export class EndpointMockingRestService extends DSpaceRESTv2Service { get(absoluteURL: string): Observable { const mockData = this.getMockData(absoluteURL); if (isEmpty(mockData)) { + console.log(absoluteURL); return super.get(absoluteURL); } else { return this.toMockResponse$(mockData); diff --git a/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts b/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts index 9a1bf4239b..3d4249ab5d 100644 --- a/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts +++ b/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts @@ -1,10 +1,10 @@ import { InjectionToken } from '@angular/core'; -import mockSuggestResponse from './mock-suggest-response.json'; +import mockSubmissionResponse from './mock-submission-response.json'; export class MockResponseMap extends Map {}; export const MOCK_RESPONSE_MAP: InjectionToken = new InjectionToken('mockResponseMap'); export const mockResponseMap: MockResponseMap = new Map([ - [ '/discover/suggestions', mockSuggestResponse ] + [ 'workspaceitems', mockSubmissionResponse ] ]); diff --git a/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json b/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json new file mode 100644 index 0000000000..0c20d30038 --- /dev/null +++ b/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json @@ -0,0 +1,377 @@ +{ + "id": 733, + "errors": [ + { + "message": "error.validation.required", + "paths": [ + "/sections/traditionalpageone/dc.title", + "/sections/traditionalpageone/dc.date.issued" + ] + } + ], + "lastModified": "2019-06-25T13:44:29.178+0000", + "sections": { + "license": { + "url": null, + "acceptanceDate": null, + "granted": false + }, + "upload": { + "files": [] + }, + "collection": "51715dd3-5590-49f2-b227-6a663c849921", + "traditionalpagetwo": {}, + "traditionalpageone": {} + }, + "type": "workspaceitem", + "_links": { + "collection": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/collection" + }, + "item": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/item" + }, + "submissionDefinition": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/submissionDefinition" + }, + "submitter": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/submitter" + }, + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733" + } + }, + "_embedded": { + "submitter": { + "id": "335647b6-8a52-4ecb-a8c1-7ebabb199bda", + "uuid": "335647b6-8a52-4ecb-a8c1-7ebabb199bda", + "name": "dspacedemo+admin@gmail.com", + "handle": null, + "metadata": { + "eperson.firstname": [ + { + "value": "Demo", + "language": null, + "authority": null, + "confidence": -1, + "place": 0 + } + ], + "eperson.language": [ + { + "value": "en", + "language": null, + "authority": null, + "confidence": -1, + "place": 0 + } + ], + "eperson.lastname": [ + { + "value": "Site Administrator", + "language": null, + "authority": null, + "confidence": -1, + "place": 0 + } + ] + }, + "netid": null, + "lastActive": "2019-06-25T13:44:28.616+0000", + "canLogIn": true, + "email": "dspacedemo+admin@gmail.com", + "requireCertificate": false, + "selfRegistered": false, + "type": "eperson", + "_links": { + "groups": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups" + }, + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda" + } + }, + "_embedded": { + "groups": null + } + }, + "item": { + "id": "0b692d3b-70b9-415f-a56d-e557f026accc", + "uuid": "0b692d3b-70b9-415f-a56d-e557f026accc", + "name": null, + "handle": null, + "metadata": {}, + "inArchive": false, + "discoverable": true, + "withdrawn": false, + "lastModified": "2019-06-25T13:44:29.160+0000", + "type": "item", + "_links": { + "bitstreams": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/bitstreams" + }, + "owningCollection": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/owningCollection" + }, + "relationships": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/relationships" + }, + "templateItemOf": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/templateItemOf" + }, + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc" + } + }, + "_embedded": { + "relationships": { + "_embedded": { + "relationships": [] + }, + "page": { + "number": 0, + "size": 0, + "totalPages": 1, + "totalElements": 0 + }, + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/relationships" + } + } + }, + "owningCollection": null, + "templateItemOf": null, + "bitstreams": { + "_embedded": { + "bitstreams": [] + }, + "page": { + "number": 0, + "size": 0, + "totalPages": 1, + "totalElements": 0 + }, + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/bitstreams" + } + } + } + } + }, + "submissionDefinition": { + "id": "traditional", + "name": "traditional", + "type": "submissiondefinition", + "isDefault": true, + "_links": { + "collections": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/collections" + }, + "sections": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/sections" + }, + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional" + } + }, + "_embedded": { + "collections": { + "_embedded": { + "collections": [] + }, + "page": { + "number": 0, + "size": 0, + "totalPages": 1, + "totalElements": 0 + }, + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/collections" + } + } + }, + "sections": { + "_embedded": { + "sections": [ + { + "id": "collection", + "mandatory": true, + "sectionType": "collection", + "visibility": { + "main": "HIDDEN", + "other": "HIDDEN" + }, + "type": "submissionsection", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/collection" + } + } + }, + { + "id": "relationships", + "header": "submit.progressbar.relationships", + "mandatory": true, + "sectionType": "relationships", + "type": "submissionsection", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/relationships" + } + } + }, + { + "id": "traditionalpageone", + "header": "submit.progressbar.describe.stepone", + "mandatory": true, + "sectionType": "submission-form", + "type": "submissionsection", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/traditionalpageone" + }, + "config": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionforms/traditionalpageone" + } + } + }, + { + "id": "traditionalpagetwo", + "header": "submit.progressbar.describe.steptwo", + "mandatory": true, + "sectionType": "submission-form", + "type": "submissionsection", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/traditionalpagetwo" + }, + "config": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionforms/traditionalpagetwo" + } + } + }, + { + "id": "upload", + "header": "submit.progressbar.upload", + "mandatory": true, + "sectionType": "upload", + "type": "submissionsection", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/upload" + }, + "config": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionuploads/upload" + } + } + }, + { + "id": "license", + "header": "submit.progressbar.license", + "mandatory": true, + "sectionType": "license", + "visibility": { + "main": null, + "other": "READONLY" + }, + "type": "submissionsection", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/license" + } + } + } + ] + }, + "page": { + "number": 0, + "size": 20, + "totalPages": 1, + "totalElements": 5 + }, + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/sections" + } + } + } + } + }, + "collection": { + "id": "51715dd3-5590-49f2-b227-6a663c849921", + "uuid": "51715dd3-5590-49f2-b227-6a663c849921", + "name": "1-step Workflow collection", + "handle": "123456789/500", + "metadata": { + "dc.description": [ + { + "value": "Start a new submission.", + "language": null, + "authority": null, + "confidence": -1, + "place": 0 + } + ], + "dc.title": [ + { + "value": "1-step Workflow collection", + "language": null, + "authority": null, + "confidence": -1, + "place": 0 + } + ] + }, + "type": "collection", + "_links": { + "license": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/license" + }, + "defaultAccessConditions": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/defaultAccessConditions" + }, + "logo": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/logo" + }, + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921" + } + }, + "_embedded": { + "logo": null, + "defaultAccessConditions": { + "_embedded": { + "defaultAccessConditions": [ + { + "id": 3649, + "name": null, + "groupUUID": "f2c7eb75-aec0-4604-ab7f-6676723818ad", + "action": "DEFAULT_BITSTREAM_READ", + "type": "resourcePolicy", + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/authz/resourcePolicies/3649" + } + } + } + ] + }, + "page": { + "number": 0, + "size": 20, + "totalPages": 1, + "totalElements": 1 + }, + "_links": { + "self": { + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/defaultAccessConditions" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/app/submission/sections/relationships/section-relationships.component.html b/src/app/submission/sections/relationships/section-relationships.component.html new file mode 100644 index 0000000000..f736fdc308 --- /dev/null +++ b/src/app/submission/sections/relationships/section-relationships.component.html @@ -0,0 +1,49 @@ +import { ChangeDetectorRef, Component, Inject } from '@angular/core'; + +import { Observable } from 'rxjs'; + +import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; +import { FormService } from '../../../shared/form/form.service'; +import { SectionModelComponent } from '../models/section.model'; +import { SectionDataObject } from '../models/section-data.model'; +import { renderSectionFor } from '../sections-decorator'; +import { SectionsType } from '../sections-type'; +import { SubmissionService } from '../../submission.service'; +import { SectionsService } from '../sections.service'; +import { CollectionDataService } from '../../../core/data/collection-data.service'; +import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; + +/** + * This component represents a section that contains a Form. + */ +@Component({ + selector: 'ds-submission-section-relationships', + styleUrls: ['./section-relationships.component.scss'], + templateUrl: './section-relationships.component.html', +}) +@renderSectionFor(SectionsType.Relationships) +export class SubmissionSectionRelationshipComponent extends SectionModelComponent { + constructor(protected changeDetectorRef: ChangeDetectorRef, + protected collectionDataService: CollectionDataService, + protected formBuilderService: FormBuilderService, + protected formService: FormService, + protected operationsBuilder: JsonPatchOperationsBuilder, + protected sectionService: SectionsService, + protected submissionService: SubmissionService, + @Inject('collectionIdProvider') public injectedCollectionId: string, + @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, + @Inject('submissionIdProvider') public injectedSubmissionId: string) { + super(injectedCollectionId, injectedSectionData, injectedSubmissionId); + } + + protected getSectionStatus(): Observable { + return undefined; + } + + protected onSectionDestroy(): void { + } + + protected onSectionInit(): void { + } + +} diff --git a/src/app/submission/sections/relationships/section-relationships.component.scss b/src/app/submission/sections/relationships/section-relationships.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/submission/sections/relationships/section-relationships.component.ts b/src/app/submission/sections/relationships/section-relationships.component.ts new file mode 100644 index 0000000000..f736fdc308 --- /dev/null +++ b/src/app/submission/sections/relationships/section-relationships.component.ts @@ -0,0 +1,49 @@ +import { ChangeDetectorRef, Component, Inject } from '@angular/core'; + +import { Observable } from 'rxjs'; + +import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; +import { FormService } from '../../../shared/form/form.service'; +import { SectionModelComponent } from '../models/section.model'; +import { SectionDataObject } from '../models/section-data.model'; +import { renderSectionFor } from '../sections-decorator'; +import { SectionsType } from '../sections-type'; +import { SubmissionService } from '../../submission.service'; +import { SectionsService } from '../sections.service'; +import { CollectionDataService } from '../../../core/data/collection-data.service'; +import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; + +/** + * This component represents a section that contains a Form. + */ +@Component({ + selector: 'ds-submission-section-relationships', + styleUrls: ['./section-relationships.component.scss'], + templateUrl: './section-relationships.component.html', +}) +@renderSectionFor(SectionsType.Relationships) +export class SubmissionSectionRelationshipComponent extends SectionModelComponent { + constructor(protected changeDetectorRef: ChangeDetectorRef, + protected collectionDataService: CollectionDataService, + protected formBuilderService: FormBuilderService, + protected formService: FormService, + protected operationsBuilder: JsonPatchOperationsBuilder, + protected sectionService: SectionsService, + protected submissionService: SubmissionService, + @Inject('collectionIdProvider') public injectedCollectionId: string, + @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, + @Inject('submissionIdProvider') public injectedSubmissionId: string) { + super(injectedCollectionId, injectedSectionData, injectedSubmissionId); + } + + protected getSectionStatus(): Observable { + return undefined; + } + + protected onSectionDestroy(): void { + } + + protected onSectionInit(): void { + } + +} diff --git a/src/app/submission/sections/sections-type.ts b/src/app/submission/sections/sections-type.ts index 02e0ba478b..34ecafe42b 100644 --- a/src/app/submission/sections/sections-type.ts +++ b/src/app/submission/sections/sections-type.ts @@ -1,4 +1,5 @@ export enum SectionsType { + Relationships = 'relationships', SubmissionForm = 'submission-form', Upload = 'upload', License = 'license', diff --git a/src/app/submission/submission.service.ts b/src/app/submission/submission.service.ts index 82185a8eae..9a71c18d09 100644 --- a/src/app/submission/submission.service.ts +++ b/src/app/submission/submission.service.ts @@ -60,6 +60,8 @@ export class SubmissionService { */ protected timer$: Observable; + private workspaceLinkPath = 'workspaceitems'; + private workflowLinkPath = 'workflowitems'; /** * Initialize service variables * @param {GlobalConfig} EnvConfig @@ -98,7 +100,7 @@ export class SubmissionService { * observable of SubmissionObject */ createSubmission(): Observable { - return this.restService.postToEndpoint('workspaceitems', {}).pipe( + return this.restService.postToEndpoint(this.workspaceLinkPath, {}).pipe( map((workspaceitem: SubmissionObject) => workspaceitem[0]), catchError(() => observableOf({}))) } @@ -116,7 +118,7 @@ export class SubmissionService { let headers = new HttpHeaders(); headers = headers.append('Content-Type', 'text/uri-list'); options.headers = headers; - return this.restService.postToEndpoint('workflowitems', selfUrl, null, options) as Observable; + return this.restService.postToEndpoint(this.workspaceLinkPath, selfUrl, null, options) as Observable; } /** @@ -306,9 +308,9 @@ export class SubmissionService { getSubmissionObjectLinkName(): string { const url = this.router.routerState.snapshot.url; if (url.startsWith('/workspaceitems') || url.startsWith('/submit')) { - return 'workspaceitems'; + return this.workspaceLinkPath; } else if (url.startsWith('/workflowitems')) { - return 'workflowitems'; + return this.workflowLinkPath; } else { return 'edititems'; } @@ -323,10 +325,10 @@ export class SubmissionService { getSubmissionScope(): SubmissionScopeType { let scope: SubmissionScopeType; switch (this.getSubmissionObjectLinkName()) { - case 'workspaceitems': + case this.workspaceLinkPath: scope = SubmissionScopeType.WorkspaceItem; break; - case 'workflowitems': + case this.workflowLinkPath: scope = SubmissionScopeType.WorkflowItem; break; } diff --git a/src/app/submission/submit/submission-submit.component.ts b/src/app/submission/submit/submission-submit.component.ts index dbfd2f5a40..448ccf97e2 100644 --- a/src/app/submission/submit/submission-submit.component.ts +++ b/src/app/submission/submit/submission-submit.component.ts @@ -56,7 +56,7 @@ export class SubmissionSubmitComponent implements OnDestroy, OnInit { * * @param {ChangeDetectorRef} changeDetectorRef * @param {NotificationsService} notificationsService - * @param {SubmissionService} submissioService + * @param {SubmissionService} submissionService * @param {Router} router * @param {TranslateService} translate * @param {ViewContainerRef} viewContainerRef @@ -64,7 +64,7 @@ export class SubmissionSubmitComponent implements OnDestroy, OnInit { constructor(private changeDetectorRef: ChangeDetectorRef, private notificationsService: NotificationsService, private router: Router, - private submissioService: SubmissionService, + private submissionService: SubmissionService, private translate: TranslateService, private viewContainerRef: ViewContainerRef) { } @@ -75,7 +75,7 @@ export class SubmissionSubmitComponent implements OnDestroy, OnInit { ngOnInit() { // NOTE execute the code on the browser side only, otherwise it is executed twice this.subs.push( - this.submissioService.createSubmission() + this.submissionService.createSubmission() .subscribe((submissionObject: SubmissionObject) => { // NOTE new submission is created on the browser side only if (isNotNull(submissionObject)) { diff --git a/yarn.lock b/yarn.lock index 9a4f96e805..f702cecf5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5582,6 +5582,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +json-loader@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== + json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" From 96959b401c1b83f3552d1beb8341438a4493adb8 Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 26 Jun 2019 16:46:15 +0200 Subject: [PATCH 003/187] added new input field type and started on lookup modal --- package.json | 1 - resources/i18n/en.json | 5 +- src/app/+search-page/search-page.module.ts | 2 +- .../dspace-rest-v2/mocks/mock-response-map.ts | 4 +- .../mocks/mock-submission-response.json | 648 ++++++++---------- ...ynamic-form-control-container.component.ts | 10 +- ...namic-lookup-relation-modal.component.html | 7 + ...dynamic-lookup-relation-modal.component.ts | 29 + .../dynamic-lookup-relation.component.html | 34 + .../dynamic-lookup-relation.component.ts | 56 ++ .../dynamic-lookup-relation.model.ts | 23 + .../parsers/lookup-relation-field-parser.ts | 17 + .../form/builder/parsers/parser-factory.ts | 4 + .../form/builder/parsers/parser-type.ts | 1 + src/app/shared/shared.module.ts | 13 +- .../section-relationships.component.html | 49 -- webpack/webpack.common.js | 4 - yarn.lock | 5 - 18 files changed, 478 insertions(+), 434 deletions(-) create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.ts create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model.ts create mode 100644 src/app/shared/form/builder/parsers/lookup-relation-field-parser.ts diff --git a/package.json b/package.json index ae4b37d6fc..7755f3d3d0 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,6 @@ "https": "1.0.0", "js-cookie": "2.2.0", "js.clone": "0.0.3", - "json-loader": "^0.5.7", "jsonschema": "1.2.2", "jwt-decode": "^2.2.0", "methods": "1.1.2", diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a066ffe9d0..727da56b1f 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -861,7 +861,10 @@ "submit": "Submit", "cancel": "Cancel", "search": "Search", - "search-help": "Click here to looking for an existing correspondence", + "lookup": "Lookup", + "add": "Add", + "lookup-help": "Click here to look up an existing relation", + "add-help": "Click here to add the current entry and to add another one", "remove": "Remove", "clear": "Clear", "clear-help": "Click here to remove the selected value", diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 65558eae17..117c94abbc 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -39,7 +39,7 @@ const effects = [ const components = [ SearchPageComponent, - SearchResultsComponent, + // SearchResultsComponent, SearchSidebarComponent, SearchSettingsComponent, ItemSearchResultGridElementComponent, diff --git a/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts b/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts index 3d4249ab5d..573a724c5a 100644 --- a/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts +++ b/src/app/core/dspace-rest-v2/mocks/mock-response-map.ts @@ -1,10 +1,10 @@ import { InjectionToken } from '@angular/core'; -import mockSubmissionResponse from './mock-submission-response.json'; +import mockSubmissionResponse from '../mocks/mock-submission-response.json'; export class MockResponseMap extends Map {}; export const MOCK_RESPONSE_MAP: InjectionToken = new InjectionToken('mockResponseMap'); export const mockResponseMap: MockResponseMap = new Map([ - [ 'workspaceitems', mockSubmissionResponse ] + [ '/config/submissionforms/traditionalpageone', mockSubmissionResponse ] ]); diff --git a/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json b/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json index 0c20d30038..1ab79f220e 100644 --- a/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json +++ b/src/app/core/dspace-rest-v2/mocks/mock-submission-response.json @@ -1,377 +1,291 @@ { - "id": 733, - "errors": [ + "id": "traditionalpageone", + "name": "traditionalpageone", + "rows": [ { - "message": "error.validation.required", - "paths": [ - "/sections/traditionalpageone/dc.title", - "/sections/traditionalpageone/dc.date.issued" + "fields": [ + { + "input": { + "type": "lookup-relation" + }, + "label": "Journal Issue", + "mandatory": true, + "repeatable": false, + "mandatoryMessage": "Required field!", + "hints": "Select a journal issue for this publication.", + "selectableMetadata": [ + { + "metadata": "relation.isPublicationOfJournalIssue", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "name" + }, + "label": "Authors", + "mandatory": false, + "repeatable": true, + "hints": "Enter the names of the authors of this item.", + "selectableMetadata": [ + { + "metadata": "dc.contributor.author", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "onebox" + }, + "label": "Title", + "mandatory": true, + "repeatable": false, + "mandatoryMessage": "You must enter a main title for this item.", + "hints": "Enter the main title of the item.", + "selectableMetadata": [ + { + "metadata": "dc.title", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "onebox" + }, + "label": "Other Titles", + "mandatory": false, + "repeatable": true, + "hints": "If the item has any alternative titles, please enter them here.", + "selectableMetadata": [ + { + "metadata": "dc.title.alternative", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "date" + }, + "label": "Date of Issue", + "mandatory": true, + "repeatable": false, + "mandatoryMessage": "You must enter at least the year.", + "hints": "Please give the date of previous publication or public distribution.\n You can leave out the day and/or month if they aren't\n applicable.", + "style": "col-sm-4", + "selectableMetadata": [ + { + "metadata": "dc.date.issued", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + }, + { + "input": { + "type": "onebox" + }, + "label": "Publisher", + "mandatory": false, + "repeatable": false, + "hints": "Enter the name of the publisher of the previously issued instance of this item.", + "style": "col-sm-8", + "selectableMetadata": [ + { + "metadata": "dc.publisher", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "onebox" + }, + "label": "Citation", + "mandatory": false, + "repeatable": false, + "hints": "Enter the standard citation for the previously issued instance of this item.", + "selectableMetadata": [ + { + "metadata": "dc.identifier.citation", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "series" + }, + "label": "Series/Report No.", + "mandatory": false, + "repeatable": true, + "hints": "Enter the series and number assigned to this item by your community.", + "selectableMetadata": [ + { + "metadata": "dc.relation.ispartofseries", + "label": null, + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "onebox" + }, + "label": "Identifiers", + "mandatory": false, + "repeatable": true, + "hints": "If the item has any identification numbers or codes associated with\nit, please enter the types and the actual numbers or codes.", + "selectableMetadata": [ + { + "metadata": "dc.identifier.issn", + "label": "ISSN", + "authority": null, + "closed": null + }, + { + "metadata": "dc.identifier.other", + "label": "Other", + "authority": null, + "closed": null + }, + { + "metadata": "dc.identifier.ismn", + "label": "ISMN", + "authority": null, + "closed": null + }, + { + "metadata": "dc.identifier.govdoc", + "label": "Gov't Doc #", + "authority": null, + "closed": null + }, + { + "metadata": "dc.identifier.uri", + "label": "URI", + "authority": null, + "closed": null + }, + { + "metadata": "dc.identifier.isbn", + "label": "ISBN", + "authority": null, + "closed": null + }, + { + "metadata": "dc.identifier.doi", + "label": "DOI", + "authority": null, + "closed": null + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "dropdown" + }, + "label": "Type", + "mandatory": false, + "repeatable": true, + "hints": "Select the type(s) of content of the item. To select more than one value in the list, you may have to hold down the \"CTRL\" or \"Shift\" key.", + "selectableMetadata": [ + { + "metadata": "dc.type", + "label": null, + "authority": "common_types", + "closed": false + } + ], + "languageCodes": [] + } + ] + }, + { + "fields": [ + { + "input": { + "type": "dropdown" + }, + "label": "Language", + "mandatory": false, + "repeatable": false, + "hints": "Select the language of the main content of the item. If the language does not appear in the list, please select 'Other'. If the content does not really have a language (for example, if it is a dataset or an image) please select 'N/A'.", + "selectableMetadata": [ + { + "metadata": "dc.language.iso", + "label": null, + "authority": "common_iso_languages", + "closed": false + } + ], + "languageCodes": [] + } ] } ], - "lastModified": "2019-06-25T13:44:29.178+0000", - "sections": { - "license": { - "url": null, - "acceptanceDate": null, - "granted": false - }, - "upload": { - "files": [] - }, - "collection": "51715dd3-5590-49f2-b227-6a663c849921", - "traditionalpagetwo": {}, - "traditionalpageone": {} - }, - "type": "workspaceitem", + "type": "submissionform", "_links": { - "collection": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/collection" - }, - "item": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/item" - }, - "submissionDefinition": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/submissionDefinition" - }, - "submitter": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733/submitter" - }, "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/submission/workspaceitems/733" - } - }, - "_embedded": { - "submitter": { - "id": "335647b6-8a52-4ecb-a8c1-7ebabb199bda", - "uuid": "335647b6-8a52-4ecb-a8c1-7ebabb199bda", - "name": "dspacedemo+admin@gmail.com", - "handle": null, - "metadata": { - "eperson.firstname": [ - { - "value": "Demo", - "language": null, - "authority": null, - "confidence": -1, - "place": 0 - } - ], - "eperson.language": [ - { - "value": "en", - "language": null, - "authority": null, - "confidence": -1, - "place": 0 - } - ], - "eperson.lastname": [ - { - "value": "Site Administrator", - "language": null, - "authority": null, - "confidence": -1, - "place": 0 - } - ] - }, - "netid": null, - "lastActive": "2019-06-25T13:44:28.616+0000", - "canLogIn": true, - "email": "dspacedemo+admin@gmail.com", - "requireCertificate": false, - "selfRegistered": false, - "type": "eperson", - "_links": { - "groups": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda/groups" - }, - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/eperson/epersons/335647b6-8a52-4ecb-a8c1-7ebabb199bda" - } - }, - "_embedded": { - "groups": null - } - }, - "item": { - "id": "0b692d3b-70b9-415f-a56d-e557f026accc", - "uuid": "0b692d3b-70b9-415f-a56d-e557f026accc", - "name": null, - "handle": null, - "metadata": {}, - "inArchive": false, - "discoverable": true, - "withdrawn": false, - "lastModified": "2019-06-25T13:44:29.160+0000", - "type": "item", - "_links": { - "bitstreams": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/bitstreams" - }, - "owningCollection": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/owningCollection" - }, - "relationships": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/relationships" - }, - "templateItemOf": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/templateItemOf" - }, - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc" - } - }, - "_embedded": { - "relationships": { - "_embedded": { - "relationships": [] - }, - "page": { - "number": 0, - "size": 0, - "totalPages": 1, - "totalElements": 0 - }, - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/relationships" - } - } - }, - "owningCollection": null, - "templateItemOf": null, - "bitstreams": { - "_embedded": { - "bitstreams": [] - }, - "page": { - "number": 0, - "size": 0, - "totalPages": 1, - "totalElements": 0 - }, - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/items/0b692d3b-70b9-415f-a56d-e557f026accc/bitstreams" - } - } - } - } - }, - "submissionDefinition": { - "id": "traditional", - "name": "traditional", - "type": "submissiondefinition", - "isDefault": true, - "_links": { - "collections": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/collections" - }, - "sections": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/sections" - }, - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional" - } - }, - "_embedded": { - "collections": { - "_embedded": { - "collections": [] - }, - "page": { - "number": 0, - "size": 0, - "totalPages": 1, - "totalElements": 0 - }, - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/collections" - } - } - }, - "sections": { - "_embedded": { - "sections": [ - { - "id": "collection", - "mandatory": true, - "sectionType": "collection", - "visibility": { - "main": "HIDDEN", - "other": "HIDDEN" - }, - "type": "submissionsection", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/collection" - } - } - }, - { - "id": "relationships", - "header": "submit.progressbar.relationships", - "mandatory": true, - "sectionType": "relationships", - "type": "submissionsection", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/relationships" - } - } - }, - { - "id": "traditionalpageone", - "header": "submit.progressbar.describe.stepone", - "mandatory": true, - "sectionType": "submission-form", - "type": "submissionsection", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/traditionalpageone" - }, - "config": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionforms/traditionalpageone" - } - } - }, - { - "id": "traditionalpagetwo", - "header": "submit.progressbar.describe.steptwo", - "mandatory": true, - "sectionType": "submission-form", - "type": "submissionsection", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/traditionalpagetwo" - }, - "config": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionforms/traditionalpagetwo" - } - } - }, - { - "id": "upload", - "header": "submit.progressbar.upload", - "mandatory": true, - "sectionType": "upload", - "type": "submissionsection", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/upload" - }, - "config": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionuploads/upload" - } - } - }, - { - "id": "license", - "header": "submit.progressbar.license", - "mandatory": true, - "sectionType": "license", - "visibility": { - "main": null, - "other": "READONLY" - }, - "type": "submissionsection", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionsections/license" - } - } - } - ] - }, - "page": { - "number": 0, - "size": 20, - "totalPages": 1, - "totalElements": 5 - }, - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissiondefinitions/traditional/sections" - } - } - } - } - }, - "collection": { - "id": "51715dd3-5590-49f2-b227-6a663c849921", - "uuid": "51715dd3-5590-49f2-b227-6a663c849921", - "name": "1-step Workflow collection", - "handle": "123456789/500", - "metadata": { - "dc.description": [ - { - "value": "Start a new submission.", - "language": null, - "authority": null, - "confidence": -1, - "place": 0 - } - ], - "dc.title": [ - { - "value": "1-step Workflow collection", - "language": null, - "authority": null, - "confidence": -1, - "place": 0 - } - ] - }, - "type": "collection", - "_links": { - "license": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/license" - }, - "defaultAccessConditions": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/defaultAccessConditions" - }, - "logo": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/logo" - }, - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921" - } - }, - "_embedded": { - "logo": null, - "defaultAccessConditions": { - "_embedded": { - "defaultAccessConditions": [ - { - "id": 3649, - "name": null, - "groupUUID": "f2c7eb75-aec0-4604-ab7f-6676723818ad", - "action": "DEFAULT_BITSTREAM_READ", - "type": "resourcePolicy", - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/authz/resourcePolicies/3649" - } - } - } - ] - }, - "page": { - "number": 0, - "size": 20, - "totalPages": 1, - "totalElements": 1 - }, - "_links": { - "self": { - "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/defaultAccessConditions" - } - } - } - } + "href": "https://dspace7.4science.cloud/dspace-spring-rest/api/config/submissionforms/traditionalpageone" } } } \ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 455c1075ef..0b81a5c7ab 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -68,10 +68,12 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model'; import { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component'; +import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION } from './models/lookup-relation/dynamic-lookup-relation.model'; +import { DsDynamicLookupRelationComponent } from './models/lookup-relation/dynamic-lookup-relation.component'; export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type | null { + console.log(model.type); switch (model.type) { - case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: return DsDynamicFormArrayComponent; @@ -125,6 +127,9 @@ export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type< case DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_NAME: return DsDynamicLookupComponent; + case DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION: + return DsDynamicLookupRelationComponent; + default: return null; } @@ -157,7 +162,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo @Output('dfFocus') focus: EventEmitter = new EventEmitter(); @Output('ngbEvent') customEvent: EventEmitter = new EventEmitter(); /* tslint:enable:no-output-rename */ - @ViewChild('componentViewContainer', {read: ViewContainerRef}) componentViewContainerRef: ViewContainerRef; + @ViewChild('componentViewContainer', { read: ViewContainerRef }) componentViewContainerRef: ViewContainerRef; private showErrorMessagesPreviousStage: boolean; @@ -166,6 +171,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo } protected test: boolean; + constructor( protected componentFactoryResolver: ComponentFactoryResolver, protected layoutService: DynamicFormLayoutService, diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html new file mode 100644 index 0000000000..6c8aeb1d95 --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html @@ -0,0 +1,7 @@ +
+ LOOKUP WINDOW FOR {{relationKey}} + +
\ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts new file mode 100644 index 0000000000..529004c930 --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit } from '@angular/core'; +import { PaginatedList } from '../../../../../../core/data/paginated-list'; +import { SearchResult } from '../../../../../../+search-page/search-result.model'; +import { RemoteData } from '../../../../../../core/data/remote-data'; +import { Observable } from 'rxjs'; +import { SearchService } from '../../../../../../+search-page/search-service/search.service'; +import { PaginatedSearchOptions } from '../../../../../../+search-page/paginated-search-options.model'; +import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model'; + +const RELATION_TYPE_FILTER_PREFIX = 'f.entityType='; + +@Component({ + selector: 'ds-dynamic-lookup-relation-modal', + // styleUrls: ['./dynamic-lookup-relation-modal.component.scss'], + templateUrl: './dynamic-lookup-relation-modal.component.html' +}) +export class DsDynamicLookupRelationModalComponent implements OnInit { + relationKey: string; + resultsRD$: Observable>>>; + + constructor(private searchService: SearchService) { + + } + + ngOnInit(): void { + this.resultsRD$ = this.searchService.search( + new PaginatedSearchOptions({ fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.relationKey })); + } +} \ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html new file mode 100644 index 0000000000..91b655ccc9 --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html @@ -0,0 +1,34 @@ +
+
+ +
+ +
+
+ +
+
+ +
+
+
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.ts new file mode 100644 index 0000000000..6209da619d --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.ts @@ -0,0 +1,56 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +import { + DynamicFormControlComponent, + DynamicFormLayoutService, + DynamicFormValidationService +} from '@ng-dynamic-forms/core'; +import { FormGroup } from '@angular/forms'; +import { DynamicRelationGroupModel } from '../relation-group/dynamic-relation-group.model'; +import { isNotEmpty } from '../../../../../empty.util'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { DsDynamicLookupRelationModalComponent } from './dynamic-lookup-relation-modal.component'; + +@Component({ + selector: 'ds-dynamic-lookup-relation', + // styleUrls: ['./dynamic-lookup-relation.component.scss'], + templateUrl: './dynamic-lookup-relation.component.html' +}) +export class DsDynamicLookupRelationComponent extends DynamicFormControlComponent implements OnInit { + + @Input() formId: string; + @Input() group: FormGroup; + @Input() model: DynamicRelationGroupModel; + + @Output() blur: EventEmitter = new EventEmitter(); + @Output() change: EventEmitter = new EventEmitter(); + @Output() focus: EventEmitter = new EventEmitter(); + + modalRef: NgbModalRef; + + constructor(private modalService: NgbModal, + protected layoutService: DynamicFormLayoutService, + protected validationService: DynamicFormValidationService + ) { + super(layoutService, validationService); + } + + ngOnInit(): void { + } + + public hasEmptyValue() { + return isNotEmpty(this.model.value); + } + + openLookup() { + this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent); + this.modalRef.componentInstance.relationKey = this.model.name; + this.modalRef.result.then((result) => { + this.model.value = result; + }); + } + + alert(t: string) { + alert(t); + } +} diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model.ts new file mode 100644 index 0000000000..e8775d6dea --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model.ts @@ -0,0 +1,23 @@ +import { DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core'; +import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model'; + +export const DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION = 'LOOKUP_RELATION'; + +export interface DynamicLookupRelationModelConfig extends DsDynamicInputModelConfig { + value?: any; +} + +export class DynamicLookupRelationModel extends DsDynamicInputModel { + + @serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION; + @serializable() value: any; + + constructor(config: DynamicLookupRelationModelConfig, layout?: DynamicFormControlLayout) { + + super(config, layout); + + this.readOnly = true; + this.disabled = true; + this.valueUpdates.next(config.value); + } +} diff --git a/src/app/shared/form/builder/parsers/lookup-relation-field-parser.ts b/src/app/shared/form/builder/parsers/lookup-relation-field-parser.ts new file mode 100644 index 0000000000..e1b3864c64 --- /dev/null +++ b/src/app/shared/form/builder/parsers/lookup-relation-field-parser.ts @@ -0,0 +1,17 @@ +import { FieldParser } from './field-parser'; +import { DynamicLookupModel, DynamicLookupModelConfig } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup.model'; +import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; +import { + DynamicLookupRelationModel, + DynamicLookupRelationModelConfig +} from '../ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model'; + +export class LookupRelationFieldParser extends FieldParser { + + public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + const lookupModelConfig: DynamicLookupRelationModelConfig = this.initModel(null, label); + + + return new DynamicLookupRelationModel(lookupModelConfig); + } +} diff --git a/src/app/shared/form/builder/parsers/parser-factory.ts b/src/app/shared/form/builder/parsers/parser-factory.ts index 2cbee18783..646b6410d2 100644 --- a/src/app/shared/form/builder/parsers/parser-factory.ts +++ b/src/app/shared/form/builder/parsers/parser-factory.ts @@ -12,6 +12,7 @@ import { NameFieldParser } from './name-field-parser'; import { SeriesFieldParser } from './series-field-parser'; import { TagFieldParser } from './tag-field-parser'; import { TextareaFieldParser } from './textarea-field-parser'; +import { LookupRelationFieldParser } from './lookup-relation-field-parser'; export class ParserFactory { public static getConstructor(type: ParserType): GenericConstructor { @@ -34,6 +35,9 @@ export class ParserFactory { case ParserType.LookupName: { return LookupNameFieldParser } + case ParserType.LookupRelation: { + return LookupRelationFieldParser + } case ParserType.Onebox: { return OneboxFieldParser } diff --git a/src/app/shared/form/builder/parsers/parser-type.ts b/src/app/shared/form/builder/parsers/parser-type.ts index a9af87d73f..f43d4654a0 100644 --- a/src/app/shared/form/builder/parsers/parser-type.ts +++ b/src/app/shared/form/builder/parsers/parser-type.ts @@ -5,6 +5,7 @@ export enum ParserType { List = 'list', Lookup = 'lookup', LookupName = 'lookup-name', + LookupRelation = 'lookup-relation', Onebox = 'onebox', Name = 'name', Series = 'series', diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 816139c8b9..ef867e8a7f 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -78,6 +78,7 @@ import { SortablejsModule } from 'angular-sortablejs'; import { NumberPickerComponent } from './number-picker/number-picker.component'; import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component'; import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component'; +import { DsDynamicLookupRelationComponent } from './form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component'; import { MockAdminGuard } from './mocks/mock-admin-guard.service'; import { AlertComponent } from './alert/alert.component'; import { MyDSpaceResultListElementComponent } from './object-list/my-dspace-result-list-element/my-dspace-result-list-element.component'; @@ -138,6 +139,8 @@ import { RoleDirective } from './roles/role.directive'; import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component'; import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component'; import { ItemDetailPreviewFieldComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; +import { DsDynamicLookupRelationModalComponent } from './form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component'; +import { SearchResultsComponent } from '../+search-page/search-results/search-results.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -198,6 +201,8 @@ const COMPONENTS = [ DsDynamicFormControlContainerComponent, DsDynamicListComponent, DsDynamicLookupComponent, + DsDynamicLookupRelationComponent, + DsDynamicLookupRelationModalComponent, DsDynamicScrollableDropdownComponent, DsDynamicTagComponent, DsDynamicTypeaheadComponent, @@ -257,7 +262,8 @@ const COMPONENTS = [ ItemSearchResultListElementComponent, TypedItemSearchResultListElementComponent, ItemTypeSwitcherComponent, - BrowseByComponent + BrowseByComponent, + SearchResultsComponent ]; const ENTRY_COMPONENTS = [ @@ -280,6 +286,8 @@ const ENTRY_COMPONENTS = [ SearchResultGridElementComponent, DsDynamicListComponent, DsDynamicLookupComponent, + DsDynamicLookupRelationComponent, + DsDynamicLookupRelationModalComponent, DsDynamicScrollableDropdownComponent, DsDynamicTagComponent, DsDynamicTypeaheadComponent, @@ -300,7 +308,8 @@ const ENTRY_COMPONENTS = [ StartsWithTextComponent, PlainTextMetadataListElementComponent, ItemMetadataListElementComponent, - MetadataRepresentationListElementComponent + MetadataRepresentationListElementComponent, + SearchResultsComponent ]; const SHARED_ITEM_PAGE_COMPONENTS = [ diff --git a/src/app/submission/sections/relationships/section-relationships.component.html b/src/app/submission/sections/relationships/section-relationships.component.html index f736fdc308..e69de29bb2 100644 --- a/src/app/submission/sections/relationships/section-relationships.component.html +++ b/src/app/submission/sections/relationships/section-relationships.component.html @@ -1,49 +0,0 @@ -import { ChangeDetectorRef, Component, Inject } from '@angular/core'; - -import { Observable } from 'rxjs'; - -import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; -import { FormService } from '../../../shared/form/form.service'; -import { SectionModelComponent } from '../models/section.model'; -import { SectionDataObject } from '../models/section-data.model'; -import { renderSectionFor } from '../sections-decorator'; -import { SectionsType } from '../sections-type'; -import { SubmissionService } from '../../submission.service'; -import { SectionsService } from '../sections.service'; -import { CollectionDataService } from '../../../core/data/collection-data.service'; -import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; - -/** - * This component represents a section that contains a Form. - */ -@Component({ - selector: 'ds-submission-section-relationships', - styleUrls: ['./section-relationships.component.scss'], - templateUrl: './section-relationships.component.html', -}) -@renderSectionFor(SectionsType.Relationships) -export class SubmissionSectionRelationshipComponent extends SectionModelComponent { - constructor(protected changeDetectorRef: ChangeDetectorRef, - protected collectionDataService: CollectionDataService, - protected formBuilderService: FormBuilderService, - protected formService: FormService, - protected operationsBuilder: JsonPatchOperationsBuilder, - protected sectionService: SectionsService, - protected submissionService: SubmissionService, - @Inject('collectionIdProvider') public injectedCollectionId: string, - @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, - @Inject('submissionIdProvider') public injectedSubmissionId: string) { - super(injectedCollectionId, injectedSectionData, injectedSubmissionId); - } - - protected getSectionStatus(): Observable { - return undefined; - } - - protected onSectionDestroy(): void { - } - - protected onSectionInit(): void { - } - -} diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 723e883462..7fb4656a15 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -91,10 +91,6 @@ module.exports = { { test: /\.html$/, loader: 'raw-loader' - }, - { - test: /\.json$/, - loader: 'json-loader' } ] }, diff --git a/yarn.lock b/yarn.lock index f702cecf5c..9a4f96e805 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5582,11 +5582,6 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-loader@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" - integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== - json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" From 58f991aaa1f7245c477381cc117b46b64474255c Mon Sep 17 00:00:00 2001 From: lotte Date: Thu, 27 Jun 2019 13:31:44 +0200 Subject: [PATCH 004/187] 62849: added search results to lookup window with checkboxes --- src/app/core/data/paginated-list.ts | 2 +- ...namic-lookup-relation-modal.component.html | 31 +++++++++++++++---- ...namic-lookup-relation-modal.component.scss | 3 ++ ...dynamic-lookup-relation-modal.component.ts | 18 ++++++++--- 4 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss diff --git a/src/app/core/data/paginated-list.ts b/src/app/core/data/paginated-list.ts index e1c1b22569..b9de67a34d 100644 --- a/src/app/core/data/paginated-list.ts +++ b/src/app/core/data/paginated-list.ts @@ -3,7 +3,7 @@ import { hasValue } from '../../shared/empty.util'; export class PaginatedList { - constructor(private pageInfo: PageInfo, + constructor(public pageInfo: PageInfo, public page: T[]) { } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html index 6c8aeb1d95..593f4e543c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html @@ -1,7 +1,26 @@ -
- LOOKUP WINDOW FOR {{relationKey}} - +
+
+
+
+
+
+ +
    +
  • + + +
  • +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss new file mode 100644 index 0000000000..9e748833f9 --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss @@ -0,0 +1,3 @@ +.result-list-element { + flex: 1; +} \ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts index 529004c930..0834696641 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts @@ -6,24 +6,34 @@ import { Observable } from 'rxjs'; import { SearchService } from '../../../../../../+search-page/search-service/search.service'; import { PaginatedSearchOptions } from '../../../../../../+search-page/paginated-search-options.model'; import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model'; +import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; const RELATION_TYPE_FILTER_PREFIX = 'f.entityType='; +/* TODO take a look at this when the REST entities submission is finished: we will probably need to get the fixed filter from the REST instead of filtering is out from the metadata field */ +const RELATION_TYPE_METADATA_PREFIX = 'relation.isPublicationOf'; + @Component({ selector: 'ds-dynamic-lookup-relation-modal', - // styleUrls: ['./dynamic-lookup-relation-modal.component.scss'], + styleUrls: ['./dynamic-lookup-relation-modal.component.scss'], templateUrl: './dynamic-lookup-relation-modal.component.html' }) export class DsDynamicLookupRelationModalComponent implements OnInit { relationKey: string; resultsRD$: Observable>>>; + searchConfig: PaginatedSearchOptions; constructor(private searchService: SearchService) { - } ngOnInit(): void { - this.resultsRD$ = this.searchService.search( - new PaginatedSearchOptions({ fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.relationKey })); + const pagination = Object.assign(new PaginationComponentOptions(), { pageSize: 5 }); + this.searchConfig = new PaginatedSearchOptions({ + pagination: pagination, + fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.relationKey.substring(RELATION_TYPE_METADATA_PREFIX.length) + }); + this.resultsRD$ = this.searchService.search(this.searchConfig); } + + onPaginationChange() {} } \ No newline at end of file From 5c39d3c8d1e79d27e1b87b4caa6fff990e820229 Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 5 Jul 2019 15:19:16 +0200 Subject: [PATCH 005/187] 62849: progress july 5 --- .../admin-sidebar/admin-sidebar.component.ts | 1 - .../data/base-response-parsing.service.ts | 1 - src/app/core/data/relationship.service.ts | 18 ++- src/app/core/shared/operators.ts | 1 - ...ynamic-form-control-container.component.ts | 1 - ...namic-lookup-relation-modal.component.html | 102 ++++++++++++---- ...namic-lookup-relation-modal.component.scss | 4 + ...dynamic-lookup-relation-modal.component.ts | 112 ++++++++++++++++-- .../dynamic-lookup-relation.component.html | 15 ++- .../dynamic-lookup-relation.component.ts | 17 ++- .../dynamic-lookup-relation.model.ts | 2 +- src/app/shared/form/form.component.html | 1 - .../shared/pagination/pagination.component.ts | 20 ++-- 13 files changed, 243 insertions(+), 52 deletions(-) diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts index 3ad1bd4272..185d083764 100644 --- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts @@ -13,7 +13,6 @@ import { combineLatest as combineLatestObservable } from 'rxjs'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model'; import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component'; -import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component'; import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 4ede02778c..0929b057db 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -44,7 +44,6 @@ export abstract class BaseResponseParsingService { } }); } - this.cache(object, requestUUID); return object; } diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index fca5074a88..81d9ed443e 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -10,7 +10,7 @@ import { getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators'; -import { DeleteRequest, RestRequest } from './request.models'; +import { DeleteRequest, PostRequest, RestRequest } from './request.models'; import { Observable } from 'rxjs/internal/Observable'; import { RestResponse } from '../cache/response.models'; import { Item } from '../shared/item.model'; @@ -64,6 +64,22 @@ export class RelationshipService { ); } + /** + * Send a post request for a relationship by ID + * @param item1 + * @param item2 + */ + addRelationship(item1: Item, item2: Item): Observable { + return this.halService.getEndpoint(this.linkPath).pipe( + isNotEmptyOperator(), + distinctUntilChanged(), + map((endpointURL: string) => new PostRequest(this.requestService.generateRequestId(), endpointURL, `${item1.self} ${item2.self}`)), + configureRequest(this.requestService), + switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)), + getResponseFromEntry() + ); + } + /** * Get a combined observable containing an array of all relationships in an item, as well as an array of the relationships their types * This is used for easier access of a relationship's type because they exist as observables diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index ae46691e39..3118549169 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -10,7 +10,6 @@ import { BrowseDefinition } from './browse-definition.model'; import { DSpaceObject } from './dspace-object.model'; import { PaginatedList } from '../data/paginated-list'; import { SearchResult } from '../../+search-page/search-result.model'; -import { Item } from './item.model'; import { Router } from '@angular/router'; /** diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 0b81a5c7ab..37e83c7657 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -72,7 +72,6 @@ import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION } from './models/lookup-relat import { DsDynamicLookupRelationComponent } from './models/lookup-relation/dynamic-lookup-relation.component'; export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type | null { - console.log(model.type); switch (model.type) { case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: return DsDynamicFormArrayComponent; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html index 593f4e543c..90f45547c6 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.html @@ -1,26 +1,88 @@ -
-
-
-
-
-
- -
    -
  • - - -
  • -
-
+ + + \ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss index 9e748833f9..337568b127 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.scss @@ -1,3 +1,7 @@ .result-list-element { flex: 1; +} + +.modal-footer { + justify-content: space-between; } \ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts index 0834696641..a13b42d634 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation-modal.component.ts @@ -2,11 +2,16 @@ import { Component, OnInit } from '@angular/core'; import { PaginatedList } from '../../../../../../core/data/paginated-list'; import { SearchResult } from '../../../../../../+search-page/search-result.model'; import { RemoteData } from '../../../../../../core/data/remote-data'; -import { Observable } from 'rxjs'; +import { asyncScheduler, Observable, ReplaySubject } from 'rxjs'; import { SearchService } from '../../../../../../+search-page/search-service/search.service'; import { PaginatedSearchOptions } from '../../../../../../+search-page/paginated-search-options.model'; import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model'; import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { hasNoValue, hasValue, isNotEmpty } from '../../../../../empty.util'; +import { getSucceededRemoteData } from '../../../../../../core/shared/operators'; +import { finalize, map, takeUntil, takeWhile } from 'rxjs/operators'; +import { concat, multicast, take } from 'rxjs/operators'; const RELATION_TYPE_FILTER_PREFIX = 'f.entityType='; @@ -20,20 +25,111 @@ const RELATION_TYPE_METADATA_PREFIX = 'relation.isPublicationOf'; }) export class DsDynamicLookupRelationModalComponent implements OnInit { relationKey: string; + fieldName: string; resultsRD$: Observable>>>; searchConfig: PaginatedSearchOptions; + repeatable: boolean = true; + selection: DSpaceObject[] = []; + allSelected = false; - constructor(private searchService: SearchService) { + constructor(protected modal: NgbActiveModal, private searchService: SearchService) { } ngOnInit(): void { - const pagination = Object.assign(new PaginationComponentOptions(), { pageSize: 5 }); - this.searchConfig = new PaginatedSearchOptions({ - pagination: pagination, - fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.relationKey.substring(RELATION_TYPE_METADATA_PREFIX.length) + this.fieldName = this.relationKey.substring(RELATION_TYPE_METADATA_PREFIX.length); + const pagination = Object.assign(new PaginationComponentOptions(), { + id: 'submission-relation-list', + pageSize: 5 }); - this.resultsRD$ = this.searchService.search(this.searchConfig); + this.onPaginationChange(pagination); } - onPaginationChange() {} + onPaginationChange(pagination: PaginationComponentOptions) { + this.searchConfig = new PaginatedSearchOptions({ + pagination: pagination, + fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.fieldName + }); + this.resultsRD$ = this.searchService.search(this.searchConfig).pipe( + /* Make sure to only listen to the first x results, until loading is finished */ + /* TODO: in Rxjs 6.4.0 and up, we can replace this by takeWhile(predicate, true) - see https://stackoverflow.com/a/44644237 */ + multicast( + () => new ReplaySubject(1), + subject => subject.pipe( + takeWhile((rd: RemoteData>>) => rd.isLoading), + concat(subject.pipe(take(1)) + ) + ) + ) as any + ) + } + + close() { + this.modal.close(this.selection); + } + + isSelected(dso: DSpaceObject): boolean { + return hasValue(this.selection.find((selected) => selected.uuid === dso.uuid)); + } + + selectCheckbox(value: boolean, dso: DSpaceObject) { + if (value) { + this.selection = [...this.selection, dso]; + } else { + this.allSelected = false; + this.selection = this.selection.filter((selected) => { + return selected.uuid !== dso.uuid + }); + } + } + + selectRadio(value: boolean, dso: DSpaceObject) { + if (value) { + this.selection = [dso]; + } + } + + selectPage(page: SearchResult[]) { + const newObjects: DSpaceObject[] = page + .map((searchResult) => searchResult.indexableObject) + .filter((dso) => hasNoValue(this.selection.find((selected) => selected.uuid === dso.uuid))); + this.selection = [...this.selection, ...newObjects] + } + + deselectPage(page: SearchResult[]) { + this.allSelected = false; + const objects: DSpaceObject[] = page + .map((searchResult) => searchResult.indexableObject); + this.selection = this.selection.filter((selected) => hasNoValue(objects.find((object) => object.uuid === selected.uuid))); + } + + selectAll() { + this.allSelected = true; + const fullPagination = Object.assign(new PaginationComponentOptions(), { + currentPage: 1, + pageSize: Number.POSITIVE_INFINITY + }); + const fullSearchConfig = Object.assign(this.searchConfig, { pagination: fullPagination }); + const results = this.searchService.search(fullSearchConfig); + results.pipe( + getSucceededRemoteData(), + map((resultsRD) => resultsRD.payload.page) + ) + .subscribe((results) => + this.selection = [...this.selection, ...results.map((searchResult) => searchResult.indexableObject)] + ); + } + + deselectAll() { + this.allSelected = false; + this.selection = []; + } + + + isAllSelected() { + return this.allSelected; + } + + isSomeSelected() { + return isNotEmpty(this.selection); + } } \ No newline at end of file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html index 91b655ccc9..9911074e09 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.component.html @@ -3,15 +3,20 @@
+ + +