mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge branch 'reorder-name-variants' into metadata-and-relationships-combined-in-submission
This commit is contained in:
107
package.json
107
package.json
@@ -11,6 +11,7 @@
|
|||||||
"node": "8.* || >= 10.*"
|
"node": "8.* || >= 10.*"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
"serialize-javascript": ">= 2.1.2",
|
||||||
"set-value": ">= 2.0.1"
|
"set-value": ">= 2.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -74,38 +75,38 @@
|
|||||||
"sync-i18n": "node ./scripts/sync-i18n-files.js"
|
"sync-i18n": "node ./scripts/sync-i18n-files.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^6.1.4",
|
"@angular/animations": "^7.2.15",
|
||||||
"@angular/cdk": "^7.3.7",
|
"@angular/cdk": "7.3.7",
|
||||||
"@angular/cli": "^6.1.5",
|
"@angular/cli": "^7.3.5",
|
||||||
"@angular/common": "^6.1.4",
|
"@angular/common": "^7.2.15",
|
||||||
"@angular/core": "^6.1.4",
|
"@angular/core": "^7.2.15",
|
||||||
"@angular/forms": "^6.1.4",
|
"@angular/forms": "^7.2.15",
|
||||||
"@angular/http": "^6.1.4",
|
"@angular/http": "^7.2.15",
|
||||||
"@angular/platform-browser": "^6.1.4",
|
"@angular/platform-browser": "^7.2.15",
|
||||||
"@angular/platform-browser-dynamic": "^6.1.4",
|
"@angular/platform-browser-dynamic": "^7.2.15",
|
||||||
"@angular/platform-server": "^6.1.4",
|
"@angular/platform-server": "^7.2.15",
|
||||||
"@angular/router": "^6.1.4",
|
"@angular/router": "^7.2.15",
|
||||||
"@angularclass/bootloader": "1.0.1",
|
"@angularclass/bootloader": "1.0.1",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^2.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^4.1.0",
|
||||||
"@ng-dynamic-forms/core": "6.2.0",
|
"@ng-dynamic-forms/core": "^7.1.0",
|
||||||
"@ng-dynamic-forms/ui-ng-bootstrap": "6.2.0",
|
"@ng-dynamic-forms/ui-ng-bootstrap": "^7.1.0",
|
||||||
"@ngrx/effects": "^6.1.0",
|
"@ngrx/effects": "^7.3.0",
|
||||||
"@ngrx/router-store": "^6.1.0",
|
"@ngrx/router-store": "^7.3.0",
|
||||||
"@ngrx/store": "^6.1.0",
|
"@ngrx/store": "^7.3.0",
|
||||||
"@nguniversal/express-engine": "6.1.0",
|
"@nguniversal/express-engine": "^7.1.1",
|
||||||
"@ngx-translate/core": "10.0.2",
|
"@ngx-translate/core": "11.0.1",
|
||||||
"@ngx-translate/http-loader": "3.0.1",
|
"@ngx-translate/http-loader": "4.0.0",
|
||||||
"@nicky-lenaers/ngx-scroll-to": "^1.0.0",
|
"@nicky-lenaers/ngx-scroll-to": "^1.0.0",
|
||||||
"angular-idle-preload": "3.0.0",
|
"angular-idle-preload": "3.0.0",
|
||||||
"angular-sortablejs": "^2.5.0",
|
"angular-sortablejs": "^2.5.0",
|
||||||
"angular2-text-mask": "9.0.0",
|
"angular2-text-mask": "9.0.0",
|
||||||
"angulartics2": "^6.2.0",
|
"angulartics2": "7.5.2",
|
||||||
"body-parser": "1.18.2",
|
"body-parser": "1.18.2",
|
||||||
"bootstrap": "4.3.1",
|
"bootstrap": "4.3.1",
|
||||||
"cerialize": "0.1.18",
|
"cerialize": "0.1.18",
|
||||||
"compression": "1.7.1",
|
"compression": "1.7.1",
|
||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
"core-js": "^2.5.7",
|
"core-js": "^2.6.5",
|
||||||
"debug-loader": "^0.0.1",
|
"debug-loader": "^0.0.1",
|
||||||
"express": "4.16.2",
|
"express": "4.16.2",
|
||||||
"express-session": "1.15.6",
|
"express-session": "1.15.6",
|
||||||
@@ -113,6 +114,7 @@
|
|||||||
"file-saver": "^1.3.8",
|
"file-saver": "^1.3.8",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^0.4.10",
|
"fork-ts-checker-webpack-plugin": "^0.4.10",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
"http-server": "0.11.1",
|
"http-server": "0.11.1",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
"js-cookie": "2.2.0",
|
"js-cookie": "2.2.0",
|
||||||
@@ -122,18 +124,19 @@
|
|||||||
"jwt-decode": "^2.2.0",
|
"jwt-decode": "^2.2.0",
|
||||||
"methods": "1.1.2",
|
"methods": "1.1.2",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
|
"moment-range": "^4.0.2",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"ng-mocks": "^6.2.1",
|
"ng-mocks": "^7.6.0",
|
||||||
"ng2-file-upload": "1.2.1",
|
"ng2-file-upload": "1.2.1",
|
||||||
"ng2-nouislider": "^1.7.11",
|
"ng2-nouislider": "^1.8.2",
|
||||||
"ngx-bootstrap": "^3.2.0",
|
"ngx-bootstrap": "^3.2.0",
|
||||||
"ngx-infinite-scroll": "6.0.1",
|
"ngx-infinite-scroll": "6.0.1",
|
||||||
"ngx-moment": "^3.1.0",
|
"ngx-moment": "^3.4.0",
|
||||||
"ngx-pagination": "3.0.3",
|
"ngx-pagination": "3.0.3",
|
||||||
"nouislider": "^11.0.0",
|
"nouislider": "^11.0.0",
|
||||||
"pem": "1.13.2",
|
"pem": "1.13.2",
|
||||||
"reflect-metadata": "0.1.12",
|
"reflect-metadata": "0.1.12",
|
||||||
"rxjs": "6.2.2",
|
"rxjs": "6.4.0",
|
||||||
"rxjs-spy": "^7.5.1",
|
"rxjs-spy": "^7.5.1",
|
||||||
"sass-resources-loader": "^2.0.0",
|
"sass-resources-loader": "^2.0.0",
|
||||||
"sortablejs": "1.7.0",
|
"sortablejs": "1.7.0",
|
||||||
@@ -143,17 +146,18 @@
|
|||||||
"url-parse": "^1.4.7",
|
"url-parse": "^1.4.7",
|
||||||
"uuid": "^3.2.1",
|
"uuid": "^3.2.1",
|
||||||
"webfontloader": "1.6.28",
|
"webfontloader": "1.6.28",
|
||||||
"webpack-cli": "^3.1.0",
|
"webpack-cli": "^3.2.0",
|
||||||
"zone.js": "^0.8.26"
|
"zone.js": "^0.8.29"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/compiler": "^6.1.4",
|
"@angular-devkit/build-angular": "^0.13.5",
|
||||||
"@angular/compiler-cli": "^6.1.4",
|
"@angular/compiler": "^7.2.15",
|
||||||
|
"@angular/compiler-cli": "^7.2.15",
|
||||||
"@fortawesome/fontawesome-free": "^5.5.0",
|
"@fortawesome/fontawesome-free": "^5.5.0",
|
||||||
"@ngrx/entity": "^6.1.0",
|
"@ngrx/entity": "^7.3.0",
|
||||||
"@ngrx/schematics": "^6.1.0",
|
"@ngrx/schematics": "^7.3.0",
|
||||||
"@ngrx/store-devtools": "^6.1.0",
|
"@ngrx/store-devtools": "^7.3.0",
|
||||||
"@ngtools/webpack": "^6.1.5",
|
"@ngtools/webpack": "^7.3.9",
|
||||||
"@schematics/angular": "^0.7.5",
|
"@schematics/angular": "^0.7.5",
|
||||||
"@types/acorn": "^4.0.3",
|
"@types/acorn": "^4.0.3",
|
||||||
"@types/cookie-parser": "1.4.1",
|
"@types/cookie-parser": "1.4.1",
|
||||||
@@ -162,44 +166,47 @@
|
|||||||
"@types/express-serve-static-core": "4.16.0",
|
"@types/express-serve-static-core": "4.16.0",
|
||||||
"@types/file-saver": "^1.3.0",
|
"@types/file-saver": "^1.3.0",
|
||||||
"@types/hammerjs": "2.0.35",
|
"@types/hammerjs": "2.0.35",
|
||||||
"@types/jasmine": "^2.8.6",
|
"@types/jasmine": "^3.3.9",
|
||||||
"@types/js-cookie": "2.1.0",
|
"@types/js-cookie": "2.1.0",
|
||||||
"@types/json5": "^0.0.30",
|
"@types/json5": "^0.0.30",
|
||||||
"@types/lodash": "^4.14.110",
|
"@types/lodash": "^4.14.110",
|
||||||
"@types/memory-cache": "0.2.0",
|
"@types/memory-cache": "0.2.0",
|
||||||
"@types/mime": "2.0.0",
|
"@types/mime": "2.0.0",
|
||||||
"@types/node": "^10.9.4",
|
"@types/node": "^11.11.2",
|
||||||
"@types/serve-static": "1.13.2",
|
"@types/serve-static": "1.13.2",
|
||||||
"@types/uuid": "^3.4.3",
|
"@types/uuid": "^3.4.3",
|
||||||
"@types/webfontloader": "1.6.29",
|
"@types/webfontloader": "1.6.29",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.12.0",
|
||||||
|
"@typescript-eslint/parser": "^2.12.0",
|
||||||
"ajv": "^6.1.1",
|
"ajv": "^6.1.1",
|
||||||
"ajv-keywords": "^3.1.0",
|
"ajv-keywords": "^3.1.0",
|
||||||
"angular2-template-loader": "0.6.2",
|
"angular2-template-loader": "0.6.2",
|
||||||
"autoprefixer": "^9.1.3",
|
"autoprefixer": "^9.1.3",
|
||||||
"caniuse-lite": "^1.0.30000697",
|
"caniuse-lite": "^1.0.30000697",
|
||||||
"cli-progress": "^3.3.1",
|
"cli-progress": "^3.3.1",
|
||||||
"codelyzer": "^4.4.4",
|
"codelyzer": "^5.1.0",
|
||||||
"commander": "^3.0.2",
|
"commander": "^3.0.2",
|
||||||
"compression-webpack-plugin": "^1.1.6",
|
"compression-webpack-plugin": "^3.0.1",
|
||||||
"copy-webpack-plugin": "^4.4.1",
|
"copy-webpack-plugin": "^5.1.1",
|
||||||
"copyfiles": "^2.1.1",
|
"copyfiles": "^2.1.1",
|
||||||
"coveralls": "3.0.0",
|
"coveralls": "3.0.0",
|
||||||
"css-loader": "1.0.0",
|
"css-loader": "3.4.0",
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^4.1.10",
|
||||||
"deep-freeze": "0.0.1",
|
"deep-freeze": "0.0.1",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
"exports-loader": "^0.7.0",
|
"exports-loader": "^0.7.0",
|
||||||
"html-webpack-plugin": "^4.0.0-alpha",
|
"html-webpack-plugin": "3.2.0",
|
||||||
"imports-loader": "0.8.0",
|
"imports-loader": "0.8.0",
|
||||||
"istanbul-instrumenter-loader": "3.0.1",
|
"istanbul-instrumenter-loader": "3.0.1",
|
||||||
"jasmine-core": "^3.2.1",
|
"jasmine-core": "^3.3.0",
|
||||||
"jasmine-marbles": "0.3.1",
|
"jasmine-marbles": "0.3.1",
|
||||||
"jasmine-spec-reporter": "4.2.1",
|
"jasmine-spec-reporter": "4.2.1",
|
||||||
"karma": "3.0.0",
|
"karma": "4.0.1",
|
||||||
"karma-chrome-launcher": "2.2.0",
|
"karma-chrome-launcher": "2.2.0",
|
||||||
"karma-cli": "1.0.1",
|
"karma-cli": "2.0.0",
|
||||||
"karma-coverage": "1.1.2",
|
"karma-coverage": "1.1.2",
|
||||||
"karma-istanbul-preprocessor": "0.0.2",
|
"karma-istanbul-preprocessor": "0.0.2",
|
||||||
"karma-jasmine": "1.1.2",
|
"karma-jasmine": "2.0.1",
|
||||||
"karma-mocha-reporter": "2.2.5",
|
"karma-mocha-reporter": "2.2.5",
|
||||||
"karma-phantomjs-launcher": "1.0.4",
|
"karma-phantomjs-launcher": "1.0.4",
|
||||||
"karma-remap-coverage": "^0.1.5",
|
"karma-remap-coverage": "^0.1.5",
|
||||||
@@ -223,26 +230,26 @@
|
|||||||
"protractor": "^5.4.2",
|
"protractor": "^5.4.2",
|
||||||
"protractor-istanbul-plugin": "2.0.0",
|
"protractor-istanbul-plugin": "2.0.0",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
"resolve-url-loader": "^2.3.0",
|
|
||||||
"rimraf": "2.6.2",
|
"rimraf": "2.6.2",
|
||||||
"rollup": "^0.65.0",
|
"rollup": "^0.65.0",
|
||||||
"rollup-plugin-commonjs": "^9.1.6",
|
"rollup-plugin-commonjs": "^9.1.6",
|
||||||
"rollup-plugin-node-globals": "1.2.1",
|
"rollup-plugin-node-globals": "1.2.1",
|
||||||
"rollup-plugin-node-resolve": "^3.0.3",
|
"rollup-plugin-node-resolve": "^3.0.3",
|
||||||
"rollup-plugin-terser": "^2.0.2",
|
"rollup-plugin-terser": "^2.0.2",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "7.3.1",
|
||||||
"script-ext-html-webpack-plugin": "2.0.1",
|
"script-ext-html-webpack-plugin": "2.1.4",
|
||||||
"source-map": "0.7.3",
|
"source-map": "0.7.3",
|
||||||
"source-map-loader": "0.2.4",
|
"source-map-loader": "0.2.4",
|
||||||
"string-replace-loader": "^2.1.1",
|
"string-replace-loader": "^2.1.1",
|
||||||
|
"terser-webpack-plugin": "^2.3.1",
|
||||||
"to-string-loader": "1.1.5",
|
"to-string-loader": "1.1.5",
|
||||||
"ts-helpers": "1.1.2",
|
"ts-helpers": "1.1.2",
|
||||||
"ts-node": "4.1.0",
|
"ts-node": "4.1.0",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "^0.9.0",
|
"typedoc": "^0.9.0",
|
||||||
"typescript": "^2.9.1",
|
"typescript": "3.1.6",
|
||||||
"webdriver-manager": "^12.1.7",
|
"webdriver-manager": "^12.1.7",
|
||||||
"webpack": "^4.17.1",
|
"webpack": "^4.29.6",
|
||||||
"webpack-bundle-analyzer": "^3.3.2",
|
"webpack-bundle-analyzer": "^3.3.2",
|
||||||
"webpack-dev-middleware": "3.2.0",
|
"webpack-dev-middleware": "3.2.0",
|
||||||
"webpack-dev-server": "^3.1.11",
|
"webpack-dev-server": "^3.1.11",
|
||||||
|
@@ -244,6 +244,8 @@
|
|||||||
|
|
||||||
"collection.create.head": "Create a Collection",
|
"collection.create.head": "Create a Collection",
|
||||||
|
|
||||||
|
"collection.create.notifications.success": "Successfully created the Collection",
|
||||||
|
|
||||||
"collection.create.sub-head": "Create a Collection for Community {{ parent }}",
|
"collection.create.sub-head": "Create a Collection for Community {{ parent }}",
|
||||||
|
|
||||||
"collection.delete.cancel": "Cancel",
|
"collection.delete.cancel": "Cancel",
|
||||||
@@ -302,6 +304,46 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"collection.edit.logo.label": "Collection logo",
|
||||||
|
|
||||||
|
"collection.edit.logo.notifications.add.error": "Uploading Collection logo failed. Please verify the content before retrying.",
|
||||||
|
|
||||||
|
"collection.edit.logo.notifications.add.success": "Upload Collection logo successful.",
|
||||||
|
|
||||||
|
"collection.edit.logo.notifications.delete.success.title": "Logo deleted",
|
||||||
|
|
||||||
|
"collection.edit.logo.notifications.delete.success.content": "Successfully deleted the collection's logo",
|
||||||
|
|
||||||
|
"collection.edit.logo.notifications.delete.error.title": "Error deleting logo",
|
||||||
|
|
||||||
|
"collection.edit.logo.upload": "Drop a Collection Logo to upload",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"collection.edit.notifications.success": "Successfully edited the Collection",
|
||||||
|
|
||||||
|
"collection.edit.return": "Return",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"collection.edit.tabs.curate.head": "Curate",
|
||||||
|
|
||||||
|
"collection.edit.tabs.curate.title": "Collection Edit - Curate",
|
||||||
|
|
||||||
|
"collection.edit.tabs.metadata.head": "Edit Metadata",
|
||||||
|
|
||||||
|
"collection.edit.tabs.metadata.title": "Collection Edit - Metadata",
|
||||||
|
|
||||||
|
"collection.edit.tabs.roles.head": "Assign Roles",
|
||||||
|
|
||||||
|
"collection.edit.tabs.roles.title": "Collection Edit - Roles",
|
||||||
|
|
||||||
|
"collection.edit.tabs.source.head": "Content Source",
|
||||||
|
|
||||||
|
"collection.edit.tabs.source.title": "Collection Edit - Content Source",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"collection.form.abstract": "Short Description",
|
"collection.form.abstract": "Short Description",
|
||||||
|
|
||||||
"collection.form.description": "Introductory text (HTML)",
|
"collection.form.description": "Introductory text (HTML)",
|
||||||
@@ -350,6 +392,8 @@
|
|||||||
|
|
||||||
"community.create.head": "Create a Community",
|
"community.create.head": "Create a Community",
|
||||||
|
|
||||||
|
"community.create.notifications.success": "Successfully created the Community",
|
||||||
|
|
||||||
"community.create.sub-head": "Create a Sub-Community for Community {{ parent }}",
|
"community.create.sub-head": "Create a Sub-Community for Community {{ parent }}",
|
||||||
|
|
||||||
"community.delete.cancel": "Cancel",
|
"community.delete.cancel": "Cancel",
|
||||||
@@ -368,6 +412,44 @@
|
|||||||
|
|
||||||
"community.edit.head": "Edit Community",
|
"community.edit.head": "Edit Community",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"community.edit.logo.label": "Community logo",
|
||||||
|
|
||||||
|
"community.edit.logo.notifications.add.error": "Uploading Community logo failed. Please verify the content before retrying.",
|
||||||
|
|
||||||
|
"community.edit.logo.notifications.add.success": "Upload Community logo successful.",
|
||||||
|
|
||||||
|
"community.edit.logo.notifications.delete.success.title": "Logo deleted",
|
||||||
|
|
||||||
|
"community.edit.logo.notifications.delete.success.content": "Successfully deleted the community's logo",
|
||||||
|
|
||||||
|
"community.edit.logo.notifications.delete.error.title": "Error deleting logo",
|
||||||
|
|
||||||
|
"community.edit.logo.upload": "Drop a Community Logo to upload",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"community.edit.notifications.success": "Successfully edited the Community",
|
||||||
|
|
||||||
|
"community.edit.return": "Return",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"community.edit.tabs.curate.head": "Curate",
|
||||||
|
|
||||||
|
"community.edit.tabs.curate.title": "Community Edit - Curate",
|
||||||
|
|
||||||
|
"community.edit.tabs.metadata.head": "Edit Metadata",
|
||||||
|
|
||||||
|
"community.edit.tabs.metadata.title": "Community Edit - Metadata",
|
||||||
|
|
||||||
|
"community.edit.tabs.roles.head": "Assign Roles",
|
||||||
|
|
||||||
|
"community.edit.tabs.roles.title": "Community Edit - Roles",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"community.form.abstract": "Short Description",
|
"community.form.abstract": "Short Description",
|
||||||
|
|
||||||
"community.form.description": "Introductory text (HTML)",
|
"community.form.description": "Introductory text (HTML)",
|
||||||
@@ -1775,7 +1857,7 @@
|
|||||||
|
|
||||||
"uploader.drag-message": "Drag & Drop your files here",
|
"uploader.drag-message": "Drag & Drop your files here",
|
||||||
|
|
||||||
"uploader.or": ", or",
|
"uploader.or": ", or ",
|
||||||
|
|
||||||
"uploader.processing": "Processing",
|
"uploader.processing": "Processing",
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { MetadataRegistryComponent } from './metadata-registry.component';
|
import { MetadataRegistryComponent } from './metadata-registry.component';
|
||||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
@@ -18,6 +17,7 @@ import { NotificationsService } from '../../../shared/notifications/notification
|
|||||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
|
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
|
||||||
|
|
||||||
describe('MetadataRegistryComponent', () => {
|
describe('MetadataRegistryComponent', () => {
|
||||||
let comp: MetadataRegistryComponent;
|
let comp: MetadataRegistryComponent;
|
||||||
@@ -101,12 +101,12 @@ describe('MetadataRegistryComponent', () => {
|
|||||||
|
|
||||||
it('should start editing the selected schema', async(() => {
|
it('should start editing the selected schema', async(() => {
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
expect(registryService.editMetadataSchema).toHaveBeenCalledWith(mockSchemasList[0]);
|
expect(registryService.editMetadataSchema).toHaveBeenCalledWith(mockSchemasList[0] as MetadataSchema);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should cancel editing the selected schema when clicked again', async(() => {
|
it('should cancel editing the selected schema when clicked again', async(() => {
|
||||||
spyOn(registryService, 'getActiveMetadataSchema').and.returnValue(observableOf(mockSchemasList[0]));
|
spyOn(registryService, 'getActiveMetadataSchema').and.returnValue(observableOf(mockSchemasList[0] as MetadataSchema));
|
||||||
spyOn(registryService, 'cancelEditMetadataSchema');
|
spyOn(registryService, 'cancelEditMetadataSchema');
|
||||||
row.click();
|
row.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -121,7 +121,7 @@ describe('MetadataRegistryComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(registryService, 'deleteMetadataSchema').and.callThrough();
|
spyOn(registryService, 'deleteMetadataSchema').and.callThrough();
|
||||||
spyOn(registryService, 'getSelectedMetadataSchemas').and.returnValue(observableOf(selectedSchemas));
|
spyOn(registryService, 'getSelectedMetadataSchemas').and.returnValue(observableOf(selectedSchemas as MetadataSchema[]));
|
||||||
comp.deleteSchemas();
|
comp.deleteSchemas();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { MetadataSchemaComponent } from './metadata-schema.component';
|
import { MetadataSchemaComponent } from './metadata-schema.component';
|
||||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
@@ -22,6 +21,7 @@ import { NotificationsServiceStub } from '../../../shared/testing/notifications-
|
|||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
|
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
|
import { MetadataField } from '../../../core/metadata/metadata-field.model';
|
||||||
|
|
||||||
describe('MetadataSchemaComponent', () => {
|
describe('MetadataSchemaComponent', () => {
|
||||||
let comp: MetadataSchemaComponent;
|
let comp: MetadataSchemaComponent;
|
||||||
@@ -152,12 +152,12 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
|
|
||||||
it('should start editing the selected field', async(() => {
|
it('should start editing the selected field', async(() => {
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
expect(registryService.editMetadataField).toHaveBeenCalledWith(mockFieldsList[2]);
|
expect(registryService.editMetadataField).toHaveBeenCalledWith(mockFieldsList[2] as MetadataField);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should cancel editing the selected field when clicked again', async(() => {
|
it('should cancel editing the selected field when clicked again', async(() => {
|
||||||
spyOn(registryService, 'getActiveMetadataField').and.returnValue(observableOf(mockFieldsList[2]));
|
spyOn(registryService, 'getActiveMetadataField').and.returnValue(observableOf(mockFieldsList[2] as MetadataField));
|
||||||
spyOn(registryService, 'cancelEditMetadataField');
|
spyOn(registryService, 'cancelEditMetadataField');
|
||||||
row.click();
|
row.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -172,7 +172,7 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(registryService, 'deleteMetadataField').and.callThrough();
|
spyOn(registryService, 'deleteMetadataField').and.callThrough();
|
||||||
spyOn(registryService, 'getSelectedMetadataFields').and.returnValue(observableOf(selectedFields));
|
spyOn(registryService, 'getSelectedMetadataFields').and.returnValue(observableOf(selectedFields as MetadataField[]));
|
||||||
comp.deleteFields();
|
comp.deleteFields();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
@@ -1,9 +1,19 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
|
import {
|
||||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
DynamicFormControlModel,
|
||||||
|
DynamicFormService,
|
||||||
|
DynamicInputModel,
|
||||||
|
DynamicTextAreaModel
|
||||||
|
} from '@ng-dynamic-forms/core';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
|
import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
|
||||||
import { NormalizedCollection } from '../../core/cache/models/normalized-collection.model';
|
import { Location } from '@angular/common';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { RequestService } from '../../core/data/request.service';
|
||||||
|
import { ObjectCacheService } from '../../core/cache/object-cache.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form used for creating and editing collections
|
* Form used for creating and editing collections
|
||||||
@@ -22,7 +32,7 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> {
|
|||||||
/**
|
/**
|
||||||
* @type {Collection.type} This is a collection-type form
|
* @type {Collection.type} This is a collection-type form
|
||||||
*/
|
*/
|
||||||
protected type = Collection.type;
|
type = Collection.type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dynamic form fields used for creating/editing a collection
|
* The dynamic form fields used for creating/editing a collection
|
||||||
@@ -65,4 +75,15 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> {
|
|||||||
name: 'dc.description.provenance',
|
name: 'dc.description.provenance',
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public constructor(protected location: Location,
|
||||||
|
protected formService: DynamicFormService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected dsoService: CommunityDataService,
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected objectCache: ObjectCacheService) {
|
||||||
|
super(location, formService, translate, notificationsService, authService, requestService, objectCache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,6 @@ import { CollectionPageComponent } from './collection-page.component';
|
|||||||
import { CollectionPageResolver } from './collection-page.resolver';
|
import { CollectionPageResolver } from './collection-page.resolver';
|
||||||
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
|
|
||||||
import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
||||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
@@ -39,12 +38,8 @@ const COLLECTION_EDIT_PATH = ':id/edit';
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: COLLECTION_EDIT_PATH,
|
path: COLLECTION_EDIT_PATH,
|
||||||
pathMatch: 'full',
|
loadChildren: './edit-collection-page/edit-collection-page.module#EditCollectionPageModule',
|
||||||
component: EditCollectionPageComponent,
|
canActivate: [AuthenticatedGuard]
|
||||||
canActivate: [AuthenticatedGuard],
|
|
||||||
resolve: {
|
|
||||||
dso: CollectionPageResolver
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id/delete',
|
path: ':id/delete',
|
||||||
|
@@ -5,15 +5,17 @@
|
|||||||
<div *ngIf="collectionRD?.payload as collection">
|
<div *ngIf="collectionRD?.payload as collection">
|
||||||
<ds-view-tracker [object]="collection"></ds-view-tracker>
|
<ds-view-tracker [object]="collection"></ds-view-tracker>
|
||||||
<header class="comcol-header border-bottom mb-4 pb-4">
|
<header class="comcol-header border-bottom mb-4 pb-4">
|
||||||
<!-- Collection logo -->
|
<!-- Collection Name -->
|
||||||
<ds-comcol-page-logo *ngIf="logoRD$"
|
|
||||||
[logo]="(logoRD$ | async)?.payload" [alternateText]="'Collection Logo'">
|
|
||||||
[alternateText]="'Collection Logo'">
|
|
||||||
</ds-comcol-page-logo>
|
|
||||||
<!-- Collection Name -->
|
|
||||||
<ds-comcol-page-header
|
<ds-comcol-page-header
|
||||||
[name]="collection.name">
|
[name]="collection.name">
|
||||||
</ds-comcol-page-header>
|
</ds-comcol-page-header>
|
||||||
|
<!-- Collection logo -->
|
||||||
|
<ds-comcol-page-logo *ngIf="logoRD$"
|
||||||
|
[logo]="(logoRD$ | async)?.payload"
|
||||||
|
[alternateText]="'Collection Logo'"
|
||||||
|
[alternateText]="'Collection Logo'">
|
||||||
|
</ds-comcol-page-logo>
|
||||||
|
|
||||||
<!-- Handle -->
|
<!-- Handle -->
|
||||||
<ds-comcol-page-handle
|
<ds-comcol-page-handle
|
||||||
[content]="collection.handle"
|
[content]="collection.handle"
|
||||||
|
@@ -7,7 +7,6 @@ import { CollectionPageComponent } from './collection-page.component';
|
|||||||
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
||||||
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
||||||
import { CollectionFormComponent } from './collection-form/collection-form.component';
|
import { CollectionFormComponent } from './collection-form/collection-form.component';
|
||||||
import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
|
|
||||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||||
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
|
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
|
||||||
import { SearchService } from '../core/shared/search/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
@@ -23,11 +22,13 @@ import { StatisticsModule } from '../statistics/statistics.module';
|
|||||||
declarations: [
|
declarations: [
|
||||||
CollectionPageComponent,
|
CollectionPageComponent,
|
||||||
CreateCollectionPageComponent,
|
CreateCollectionPageComponent,
|
||||||
EditCollectionPageComponent,
|
|
||||||
DeleteCollectionPageComponent,
|
DeleteCollectionPageComponent,
|
||||||
CollectionFormComponent,
|
CollectionFormComponent,
|
||||||
CollectionItemMapperComponent
|
CollectionItemMapperComponent
|
||||||
],
|
],
|
||||||
|
exports: [
|
||||||
|
CollectionFormComponent
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SearchService,
|
SearchService,
|
||||||
]
|
]
|
||||||
|
@@ -4,5 +4,5 @@
|
|||||||
<h2 id="sub-header" class="border-bottom pb-2">{{'collection.create.sub-head' | translate:{ parent: (parentRD$| async)?.payload.name } }}</h2>
|
<h2 id="sub-header" class="border-bottom pb-2">{{'collection.create.sub-head' | translate:{ parent: (parentRD$| async)?.payload.name } }}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ds-collection-form (submitForm)="onSubmit($event)"></ds-collection-form>
|
<ds-collection-form (submitForm)="onSubmit($event)" (finish)="navigateToNewPage()"></ds-collection-form>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,6 +10,8 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
|
|||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
import { CreateCollectionPageComponent } from './create-collection-page.component';
|
import { CreateCollectionPageComponent } from './create-collection-page.component';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||||
|
|
||||||
describe('CreateCollectionPageComponent', () => {
|
describe('CreateCollectionPageComponent', () => {
|
||||||
let comp: CreateCollectionPageComponent;
|
let comp: CreateCollectionPageComponent;
|
||||||
@@ -27,6 +29,7 @@ describe('CreateCollectionPageComponent', () => {
|
|||||||
},
|
},
|
||||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
||||||
{ provide: Router, useValue: {} },
|
{ provide: Router, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -5,6 +5,8 @@ import { Router } from '@angular/router';
|
|||||||
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can create a new Collection
|
* Component that represents the page where a user can create a new Collection
|
||||||
@@ -16,13 +18,16 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
|
|||||||
})
|
})
|
||||||
export class CreateCollectionPageComponent extends CreateComColPageComponent<Collection> {
|
export class CreateCollectionPageComponent extends CreateComColPageComponent<Collection> {
|
||||||
protected frontendURL = '/collections/';
|
protected frontendURL = '/collections/';
|
||||||
|
protected type = Collection.type;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected communityDataService: CommunityDataService,
|
protected communityDataService: CommunityDataService,
|
||||||
protected collectionDataService: CollectionDataService,
|
protected collectionDataService: CollectionDataService,
|
||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService
|
||||||
) {
|
) {
|
||||||
super(collectionDataService, communityDataService, routeService, router);
|
super(collectionDataService, communityDataService, routeService, router, notificationsService, translate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for managing a collection's curation tasks
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-collection-curate',
|
||||||
|
templateUrl: './collection-curate.component.html',
|
||||||
|
})
|
||||||
|
export class CollectionCurateComponent {
|
||||||
|
/* TODO: Implement Collection Edit - Curate */
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
<ds-collection-form (submitForm)="onSubmit($event)"
|
||||||
|
[dso]="(dsoRD$ | async)?.payload"
|
||||||
|
(finish)="navigateToHomePage()"></ds-collection-form>
|
||||||
|
<a class="btn btn-danger"
|
||||||
|
[routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'collection.edit.delete'
|
||||||
|
| translate}}</a>
|
@@ -0,0 +1,42 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { SharedModule } from '../../../shared/shared.module';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { CollectionMetadataComponent } from './collection-metadata.component';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
|
|
||||||
|
describe('CollectionMetadataComponent', () => {
|
||||||
|
let comp: CollectionMetadataComponent;
|
||||||
|
let fixture: ComponentFixture<CollectionMetadataComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
|
declarations: [CollectionMetadataComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: CollectionDataService, useValue: {} },
|
||||||
|
{ provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CollectionMetadataComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('frontendURL', () => {
|
||||||
|
it('should have the right frontendURL set', () => {
|
||||||
|
expect((comp as any).frontendURL).toEqual('/collections/');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
|
||||||
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for editing a collection's metadata
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-collection-metadata',
|
||||||
|
templateUrl: './collection-metadata.component.html',
|
||||||
|
})
|
||||||
|
export class CollectionMetadataComponent extends ComcolMetadataComponent<Collection> {
|
||||||
|
protected frontendURL = '/collections/';
|
||||||
|
protected type = Collection.type;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
protected collectionDataService: CollectionDataService,
|
||||||
|
protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService
|
||||||
|
) {
|
||||||
|
super(collectionDataService, router, route, notificationsService, translate);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for managing a collection's roles
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-collection-roles',
|
||||||
|
templateUrl: './collection-roles.component.html',
|
||||||
|
})
|
||||||
|
export class CollectionRolesComponent {
|
||||||
|
/* TODO: Implement Collection Edit - Roles */
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for managing the content source of the collection
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-collection-source',
|
||||||
|
templateUrl: './collection-source.component.html',
|
||||||
|
})
|
||||||
|
export class CollectionSourceComponent {
|
||||||
|
/* TODO: Implement Collection Edit - Content Source */
|
||||||
|
}
|
@@ -1,11 +0,0 @@
|
|||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 pb-4">
|
|
||||||
<h2 id="header" class="border-bottom pb-2">{{ 'collection.edit.head' | translate }}</h2>
|
|
||||||
<ds-collection-form (submitForm)="onSubmit($event)" [dso]="(dsoRD$ | async)?.payload"></ds-collection-form>
|
|
||||||
<a class="btn btn-danger"
|
|
||||||
[routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'collection.edit.delete'
|
|
||||||
| translate}}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@@ -1 +0,0 @@
|
|||||||
|
|
@@ -13,13 +13,29 @@ describe('EditCollectionPageComponent', () => {
|
|||||||
let comp: EditCollectionPageComponent;
|
let comp: EditCollectionPageComponent;
|
||||||
let fixture: ComponentFixture<EditCollectionPageComponent>;
|
let fixture: ComponentFixture<EditCollectionPageComponent>;
|
||||||
|
|
||||||
|
const routeStub = {
|
||||||
|
data: observableOf({
|
||||||
|
dso: { payload: {} }
|
||||||
|
}),
|
||||||
|
routeConfig: {
|
||||||
|
children: []
|
||||||
|
},
|
||||||
|
snapshot: {
|
||||||
|
firstChild: {
|
||||||
|
routeConfig: {
|
||||||
|
path: 'mockUrl'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
declarations: [EditCollectionPageComponent],
|
declarations: [EditCollectionPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CollectionDataService, useValue: {} },
|
{ provide: CollectionDataService, useValue: {} },
|
||||||
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -31,9 +47,9 @@ describe('EditCollectionPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('frontendURL', () => {
|
describe('type', () => {
|
||||||
it('should have the right frontendURL set', () => {
|
it('should have the right type set', () => {
|
||||||
expect((comp as any).frontendURL).toEqual('/collections/');
|
expect((comp as any).type).toEqual('collection');
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -2,24 +2,30 @@ import { Component } from '@angular/core';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { getCollectionPageRoute } from '../collection-page-routing.module';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can edit an existing Collection
|
* Component that represents the page where a user can edit an existing Collection
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-collection',
|
selector: 'ds-edit-collection',
|
||||||
styleUrls: ['./edit-collection-page.component.scss'],
|
templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html'
|
||||||
templateUrl: './edit-collection-page.component.html'
|
|
||||||
})
|
})
|
||||||
export class EditCollectionPageComponent extends EditComColPageComponent<Collection> {
|
export class EditCollectionPageComponent extends EditComColPageComponent<Collection> {
|
||||||
protected frontendURL = '/collections/';
|
type = 'collection';
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected collectionDataService: CollectionDataService,
|
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected route: ActivatedRoute
|
protected route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
super(collectionDataService, router, route);
|
super(router, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the collection page url
|
||||||
|
* @param collection The collection for which the url is requested
|
||||||
|
*/
|
||||||
|
getPageUrl(collection: Collection): string {
|
||||||
|
return getCollectionPageRoute(collection.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { EditCollectionPageComponent } from './edit-collection-page.component';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
import { EditCollectionPageRoutingModule } from './edit-collection-page.routing.module';
|
||||||
|
import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
|
||||||
|
import { CollectionPageModule } from '../collection-page.module';
|
||||||
|
import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
|
||||||
|
import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
|
||||||
|
import { CollectionSourceComponent } from './collection-source/collection-source.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module that contains all components related to the Edit Collection page administrator functionality
|
||||||
|
*/
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
EditCollectionPageRoutingModule,
|
||||||
|
CollectionPageModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
EditCollectionPageComponent,
|
||||||
|
CollectionMetadataComponent,
|
||||||
|
CollectionRolesComponent,
|
||||||
|
CollectionCurateComponent,
|
||||||
|
CollectionSourceComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class EditCollectionPageModule {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,61 @@
|
|||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { EditCollectionPageComponent } from './edit-collection-page.component';
|
||||||
|
import { CollectionPageResolver } from '../collection-page.resolver';
|
||||||
|
import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
|
||||||
|
import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
|
||||||
|
import { CollectionSourceComponent } from './collection-source/collection-source.component';
|
||||||
|
import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routing module that handles the routing for the Edit Collection page administrator functionality
|
||||||
|
*/
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: EditCollectionPageComponent,
|
||||||
|
resolve: {
|
||||||
|
dso: CollectionPageResolver
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'metadata',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'metadata',
|
||||||
|
component: CollectionMetadataComponent,
|
||||||
|
data: {
|
||||||
|
title: 'collection.edit.tabs.metadata.title',
|
||||||
|
hideReturnButton: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'roles',
|
||||||
|
component: CollectionRolesComponent,
|
||||||
|
data: { title: 'collection.edit.tabs.roles.title' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'source',
|
||||||
|
component: CollectionSourceComponent,
|
||||||
|
data: { title: 'collection.edit.tabs.source.title' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'curate',
|
||||||
|
component: CollectionCurateComponent,
|
||||||
|
data: { title: 'collection.edit.tabs.curate.title' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CollectionPageResolver,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class EditCollectionPageRoutingModule {
|
||||||
|
|
||||||
|
}
|
@@ -1,9 +1,19 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
|
import {
|
||||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
DynamicFormControlModel,
|
||||||
|
DynamicFormService,
|
||||||
|
DynamicInputModel,
|
||||||
|
DynamicTextAreaModel
|
||||||
|
} from '@ng-dynamic-forms/core';
|
||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
import { ResourceType } from '../../core/shared/resource-type';
|
|
||||||
import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
|
import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { RequestService } from '../../core/data/request.service';
|
||||||
|
import { ObjectCacheService } from '../../core/cache/object-cache.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form used for creating and editing communities
|
* Form used for creating and editing communities
|
||||||
@@ -22,7 +32,7 @@ export class CommunityFormComponent extends ComColFormComponent<Community> {
|
|||||||
/**
|
/**
|
||||||
* @type {Community.type} This is a community-type form
|
* @type {Community.type} This is a community-type form
|
||||||
*/
|
*/
|
||||||
protected type = Community.type;
|
type = Community.type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dynamic form fields used for creating/editing a community
|
* The dynamic form fields used for creating/editing a community
|
||||||
@@ -57,4 +67,15 @@ export class CommunityFormComponent extends ComColFormComponent<Community> {
|
|||||||
name: 'dc.description.tableofcontents',
|
name: 'dc.description.tableofcontents',
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public constructor(protected location: Location,
|
||||||
|
protected formService: DynamicFormService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected dsoService: CommunityDataService,
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected objectCache: ObjectCacheService) {
|
||||||
|
super(location, formService, translate, notificationsService, authService, requestService, objectCache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,6 @@ import { CommunityPageComponent } from './community-page.component';
|
|||||||
import { CommunityPageResolver } from './community-page.resolver';
|
import { CommunityPageResolver } from './community-page.resolver';
|
||||||
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component';
|
|
||||||
import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
||||||
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
@@ -38,12 +37,8 @@ const COMMUNITY_EDIT_PATH = ':id/edit';
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: COMMUNITY_EDIT_PATH,
|
path: COMMUNITY_EDIT_PATH,
|
||||||
pathMatch: 'full',
|
loadChildren: './edit-community-page/edit-community-page.module#EditCommunityPageModule',
|
||||||
component: EditCommunityPageComponent,
|
canActivate: [AuthenticatedGuard]
|
||||||
canActivate: [AuthenticatedGuard],
|
|
||||||
resolve: {
|
|
||||||
dso: CommunityPageResolver
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id/delete',
|
path: ':id/delete',
|
||||||
|
@@ -3,12 +3,11 @@
|
|||||||
<div *ngIf="communityRD?.payload; let communityPayload">
|
<div *ngIf="communityRD?.payload; let communityPayload">
|
||||||
<ds-view-tracker [object]="communityPayload"></ds-view-tracker>
|
<ds-view-tracker [object]="communityPayload"></ds-view-tracker>
|
||||||
<header class="comcol-header border-bottom mb-4 pb-4">
|
<header class="comcol-header border-bottom mb-4 pb-4">
|
||||||
|
<!-- Community name -->
|
||||||
|
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
|
||||||
<!-- Community logo -->
|
<!-- Community logo -->
|
||||||
<ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'Community Logo'">
|
<ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'Community Logo'">
|
||||||
</ds-comcol-page-logo>
|
</ds-comcol-page-logo>
|
||||||
|
|
||||||
<!-- Community name -->
|
|
||||||
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
|
|
||||||
<!-- Handle -->
|
<!-- Handle -->
|
||||||
<ds-comcol-page-handle [content]="communityPayload.handle" [title]="'community.page.handle'">
|
<ds-comcol-page-handle [content]="communityPayload.handle" [title]="'community.page.handle'">
|
||||||
</ds-comcol-page-handle>
|
</ds-comcol-page-handle>
|
||||||
|
@@ -9,7 +9,6 @@ import { CommunityPageRoutingModule } from './community-page-routing.module';
|
|||||||
import { CommunityPageSubCommunityListComponent } from './sub-community-list/community-page-sub-community-list.component';
|
import { CommunityPageSubCommunityListComponent } from './sub-community-list/community-page-sub-community-list.component';
|
||||||
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
||||||
import { CommunityFormComponent } from './community-form/community-form.component';
|
import { CommunityFormComponent } from './community-form/community-form.component';
|
||||||
import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component';
|
|
||||||
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
||||||
import { StatisticsModule } from '../statistics/statistics.module';
|
import { StatisticsModule } from '../statistics/statistics.module';
|
||||||
|
|
||||||
@@ -25,9 +24,11 @@ import { StatisticsModule } from '../statistics/statistics.module';
|
|||||||
CommunityPageSubCollectionListComponent,
|
CommunityPageSubCollectionListComponent,
|
||||||
CommunityPageSubCommunityListComponent,
|
CommunityPageSubCommunityListComponent,
|
||||||
CreateCommunityPageComponent,
|
CreateCommunityPageComponent,
|
||||||
EditCommunityPageComponent,
|
|
||||||
DeleteCommunityPageComponent,
|
DeleteCommunityPageComponent,
|
||||||
CommunityFormComponent
|
CommunityFormComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CommunityFormComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -7,5 +7,5 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ds-community-form (submitForm)="onSubmit($event)"></ds-community-form>
|
<ds-community-form (submitForm)="onSubmit($event)" (finish)="navigateToNewPage()"></ds-community-form>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,6 +10,8 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
|
|||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
import { CreateCommunityPageComponent } from './create-community-page.component';
|
import { CreateCommunityPageComponent } from './create-community-page.component';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||||
|
|
||||||
describe('CreateCommunityPageComponent', () => {
|
describe('CreateCommunityPageComponent', () => {
|
||||||
let comp: CreateCommunityPageComponent;
|
let comp: CreateCommunityPageComponent;
|
||||||
@@ -23,6 +25,7 @@ describe('CreateCommunityPageComponent', () => {
|
|||||||
{ provide: CommunityDataService, useValue: { findById: () => observableOf({}) } },
|
{ provide: CommunityDataService, useValue: { findById: () => observableOf({}) } },
|
||||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
||||||
{ provide: Router, useValue: {} },
|
{ provide: Router, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -4,6 +4,8 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
|||||||
import { RouteService } from '../../core/services/route.service';
|
import { RouteService } from '../../core/services/route.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can create a new Community
|
* Component that represents the page where a user can create a new Community
|
||||||
@@ -15,12 +17,15 @@ import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comc
|
|||||||
})
|
})
|
||||||
export class CreateCommunityPageComponent extends CreateComColPageComponent<Community> {
|
export class CreateCommunityPageComponent extends CreateComColPageComponent<Community> {
|
||||||
protected frontendURL = '/communities/';
|
protected frontendURL = '/communities/';
|
||||||
|
protected type = Community.type;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected communityDataService: CommunityDataService,
|
protected communityDataService: CommunityDataService,
|
||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService
|
||||||
) {
|
) {
|
||||||
super(communityDataService, communityDataService, routeService, router);
|
super(communityDataService, communityDataService, routeService, router, notificationsService, translate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for managing a community's curation tasks
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-curate',
|
||||||
|
templateUrl: './community-curate.component.html',
|
||||||
|
})
|
||||||
|
export class CommunityCurateComponent {
|
||||||
|
/* TODO: Implement Community Edit - Curate */
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
<ds-community-form (submitForm)="onSubmit($event)"
|
||||||
|
[dso]="(dsoRD$ | async)?.payload"
|
||||||
|
(finish)="navigateToHomePage()"></ds-community-form>
|
||||||
|
<a class="btn btn-danger"
|
||||||
|
[routerLink]="'/communities/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'community.edit.delete'
|
||||||
|
| translate}}</a>
|
@@ -0,0 +1,42 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { SharedModule } from '../../../shared/shared.module';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { CommunityMetadataComponent } from './community-metadata.component';
|
||||||
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
|
|
||||||
|
describe('CommunityMetadataComponent', () => {
|
||||||
|
let comp: CommunityMetadataComponent;
|
||||||
|
let fixture: ComponentFixture<CommunityMetadataComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
|
declarations: [CommunityMetadataComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: CommunityDataService, useValue: {} },
|
||||||
|
{ provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CommunityMetadataComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('frontendURL', () => {
|
||||||
|
it('should have the right frontendURL set', () => {
|
||||||
|
expect((comp as any).frontendURL).toEqual('/communities/');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Community } from '../../../core/shared/community.model';
|
||||||
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for editing a community's metadata
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-metadata',
|
||||||
|
templateUrl: './community-metadata.component.html',
|
||||||
|
})
|
||||||
|
export class CommunityMetadataComponent extends ComcolMetadataComponent<Community> {
|
||||||
|
protected frontendURL = '/communities/';
|
||||||
|
protected type = Community.type;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
protected communityDataService: CommunityDataService,
|
||||||
|
protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService
|
||||||
|
) {
|
||||||
|
super(communityDataService, router, route, notificationsService, translate);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for managing a community's roles
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-roles',
|
||||||
|
templateUrl: './community-roles.component.html',
|
||||||
|
})
|
||||||
|
export class CommunityRolesComponent {
|
||||||
|
/* TODO: Implement Community Edit - Roles */
|
||||||
|
}
|
@@ -1,12 +0,0 @@
|
|||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 pb-4">
|
|
||||||
<h2 id="header" class="border-bottom pb-2">{{ 'community.edit.head' | translate }}</h2>
|
|
||||||
<ds-community-form (submitForm)="onSubmit($event)"
|
|
||||||
[dso]="(dsoRD$ | async)?.payload"></ds-community-form>
|
|
||||||
<a class="btn btn-danger"
|
|
||||||
[routerLink]="'/communities/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'community.edit.delete'
|
|
||||||
| translate}}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@@ -1 +0,0 @@
|
|||||||
|
|
@@ -13,13 +13,29 @@ describe('EditCommunityPageComponent', () => {
|
|||||||
let comp: EditCommunityPageComponent;
|
let comp: EditCommunityPageComponent;
|
||||||
let fixture: ComponentFixture<EditCommunityPageComponent>;
|
let fixture: ComponentFixture<EditCommunityPageComponent>;
|
||||||
|
|
||||||
|
const routeStub = {
|
||||||
|
data: observableOf({
|
||||||
|
dso: { payload: {} }
|
||||||
|
}),
|
||||||
|
routeConfig: {
|
||||||
|
children: []
|
||||||
|
},
|
||||||
|
snapshot: {
|
||||||
|
firstChild: {
|
||||||
|
routeConfig: {
|
||||||
|
path: 'mockUrl'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
declarations: [EditCommunityPageComponent],
|
declarations: [EditCommunityPageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CommunityDataService, useValue: {} },
|
{ provide: CommunityDataService, useValue: {} },
|
||||||
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -31,9 +47,9 @@ describe('EditCommunityPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('frontendURL', () => {
|
describe('type', () => {
|
||||||
it('should have the right frontendURL set', () => {
|
it('should have the right type set', () => {
|
||||||
expect((comp as any).frontendURL).toEqual('/communities/');
|
expect((comp as any).type).toEqual('community');
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,25 +1,31 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
||||||
|
import { getCommunityPageRoute } from '../community-page-routing.module';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can edit an existing Community
|
* Component that represents the page where a user can edit an existing Community
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-community',
|
selector: 'ds-edit-community',
|
||||||
styleUrls: ['./edit-community-page.component.scss'],
|
templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html'
|
||||||
templateUrl: './edit-community-page.component.html'
|
|
||||||
})
|
})
|
||||||
export class EditCommunityPageComponent extends EditComColPageComponent<Community> {
|
export class EditCommunityPageComponent extends EditComColPageComponent<Community> {
|
||||||
protected frontendURL = '/communities/';
|
type = 'community';
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected communityDataService: CommunityDataService,
|
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected route: ActivatedRoute
|
protected route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
super(communityDataService, router, route);
|
super(router, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the community page url
|
||||||
|
* @param community The community for which the url is requested
|
||||||
|
*/
|
||||||
|
getPageUrl(community: Community): string {
|
||||||
|
return getCommunityPageRoute(community.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,30 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
import { EditCommunityPageRoutingModule } from './edit-community-page.routing.module';
|
||||||
|
import { CommunityPageModule } from '../community-page.module';
|
||||||
|
import { EditCommunityPageComponent } from './edit-community-page.component';
|
||||||
|
import { CommunityCurateComponent } from './community-curate/community-curate.component';
|
||||||
|
import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
|
||||||
|
import { CommunityRolesComponent } from './community-roles/community-roles.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module that contains all components related to the Edit Community page administrator functionality
|
||||||
|
*/
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
EditCommunityPageRoutingModule,
|
||||||
|
CommunityPageModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
EditCommunityPageComponent,
|
||||||
|
CommunityCurateComponent,
|
||||||
|
CommunityMetadataComponent,
|
||||||
|
CommunityRolesComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class EditCommunityPageModule {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
import { CommunityPageResolver } from '../community-page.resolver';
|
||||||
|
import { EditCommunityPageComponent } from './edit-community-page.component';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
|
||||||
|
import { CommunityRolesComponent } from './community-roles/community-roles.component';
|
||||||
|
import { CommunityCurateComponent } from './community-curate/community-curate.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routing module that handles the routing for the Edit Community page administrator functionality
|
||||||
|
*/
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: EditCommunityPageComponent,
|
||||||
|
resolve: {
|
||||||
|
dso: CommunityPageResolver
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'metadata',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'metadata',
|
||||||
|
component: CommunityMetadataComponent,
|
||||||
|
data: {
|
||||||
|
title: 'community.edit.tabs.metadata.title',
|
||||||
|
hideReturnButton: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'roles',
|
||||||
|
component: CommunityRolesComponent,
|
||||||
|
data: { title: 'community.edit.tabs.roles.title' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'curate',
|
||||||
|
component: CommunityCurateComponent,
|
||||||
|
data: { title: 'community.edit.tabs.curate.title' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CommunityPageResolver,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class EditCommunityPageRoutingModule {
|
||||||
|
|
||||||
|
}
|
@@ -33,12 +33,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
|||||||
/**
|
/**
|
||||||
* The UploaderOptions object
|
* The UploaderOptions object
|
||||||
*/
|
*/
|
||||||
public uploadFilesOptions: UploaderOptions = {
|
public uploadFilesOptions: UploaderOptions = new UploaderOptions();
|
||||||
url: '',
|
|
||||||
authToken: null,
|
|
||||||
disableMultipart: false,
|
|
||||||
itemAlias: null
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription to unsubscribe from
|
* Subscription to unsubscribe from
|
||||||
|
@@ -82,6 +82,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
angulartics2GoogleAnalytics.startTracking();
|
||||||
angulartics2DSpace.startTracking();
|
angulartics2DSpace.startTracking();
|
||||||
|
|
||||||
metadata.listenForRouteChange();
|
metadata.listenForRouteChange();
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
|
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
|
||||||
import * as fromRouter from '@ngrx/router-store';
|
import * as fromRouter from '@ngrx/router-store';
|
||||||
|
|
||||||
import { hostWindowReducer, HostWindowState } from './shared/search/host-window.reducer';
|
import { hostWindowReducer, HostWindowState } from './shared/search/host-window.reducer';
|
||||||
import { CommunityListReducer, CommunityListState } from './community-list-page/community-list.reducer';
|
import { CommunityListReducer, CommunityListState } from './community-list-page/community-list.reducer';
|
||||||
import { formReducer, FormState } from './shared/form/form.reducer';
|
import { formReducer, FormState } from './shared/form/form.reducer';
|
||||||
@@ -8,23 +9,28 @@ import { sidebarFilterReducer, SidebarFiltersState } from './shared/sidebar/filt
|
|||||||
import { filterReducer, SearchFiltersState } from './shared/search/search-filters/search-filter/search-filter.reducer';
|
import { filterReducer, SearchFiltersState } from './shared/search/search-filters/search-filter/search-filter.reducer';
|
||||||
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
||||||
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||||
import { metadataRegistryReducer, MetadataRegistryState } from './+admin/admin-registries/metadata-registry/metadata-registry.reducers';
|
import {
|
||||||
|
metadataRegistryReducer,
|
||||||
|
MetadataRegistryState
|
||||||
|
} from './+admin/admin-registries/metadata-registry/metadata-registry.reducers';
|
||||||
import { hasValue } from './shared/empty.util';
|
import { hasValue } from './shared/empty.util';
|
||||||
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
|
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
|
||||||
import { menusReducer, MenusState } from './shared/menu/menu.reducer';
|
import { menusReducer, MenusState } from './shared/menu/menu.reducer';
|
||||||
import { historyReducer, HistoryState } from './shared/history/history.reducer';
|
import {
|
||||||
import { selectableListReducer, SelectableListsState } from './shared/object-list/selectable-list/selectable-list.reducer';
|
selectableListReducer,
|
||||||
import { bitstreamFormatReducer, BitstreamFormatRegistryState } from './+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
SelectableListsState
|
||||||
|
} from './shared/object-list/selectable-list/selectable-list.reducer';
|
||||||
import { ObjectSelectionListState, objectSelectionReducer } from './shared/object-select/object-select.reducer';
|
import { ObjectSelectionListState, objectSelectionReducer } from './shared/object-select/object-select.reducer';
|
||||||
import { NameVariantListsState, nameVariantReducer } from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
import {
|
||||||
|
NameVariantListsState,
|
||||||
|
nameVariantReducer
|
||||||
|
} from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
router: fromRouter.RouterReducerState;
|
router: fromRouter.RouterReducerState;
|
||||||
history: HistoryState;
|
|
||||||
hostWindow: HostWindowState;
|
hostWindow: HostWindowState;
|
||||||
forms: FormState;
|
forms: FormState;
|
||||||
metadataRegistry: MetadataRegistryState;
|
metadataRegistry: MetadataRegistryState;
|
||||||
bitstreamFormats: BitstreamFormatRegistryState;
|
|
||||||
notifications: NotificationsState;
|
notifications: NotificationsState;
|
||||||
sidebar: SidebarState;
|
sidebar: SidebarState;
|
||||||
sidebarFilter: SidebarFiltersState;
|
sidebarFilter: SidebarFiltersState;
|
||||||
@@ -40,11 +46,9 @@ export interface AppState {
|
|||||||
|
|
||||||
export const appReducers: ActionReducerMap<AppState> = {
|
export const appReducers: ActionReducerMap<AppState> = {
|
||||||
router: fromRouter.routerReducer,
|
router: fromRouter.routerReducer,
|
||||||
history: historyReducer,
|
|
||||||
hostWindow: hostWindowReducer,
|
hostWindow: hostWindowReducer,
|
||||||
forms: formReducer,
|
forms: formReducer,
|
||||||
metadataRegistry: metadataRegistryReducer,
|
metadataRegistry: metadataRegistryReducer,
|
||||||
bitstreamFormats: bitstreamFormatReducer,
|
|
||||||
notifications: notificationsReducer,
|
notifications: notificationsReducer,
|
||||||
sidebar: sidebarReducer,
|
sidebar: sidebarReducer,
|
||||||
sidebarFilter: sidebarFilterReducer,
|
sidebarFilter: sidebarFilterReducer,
|
||||||
|
@@ -312,7 +312,7 @@ export class CommunityListService {
|
|||||||
|
|
||||||
hasColls$ = this.collectionDataService.findByParent(community.uuid, { elementsPerPage: 1 })
|
hasColls$ = this.collectionDataService.findByParent(community.uuid, { elementsPerPage: 1 })
|
||||||
.pipe(
|
.pipe(
|
||||||
filter((rd: RemoteData<PaginatedList<Community>>) => rd.hasSucceeded),
|
filter((rd: RemoteData<PaginatedList<Collection>>) => rd.hasSucceeded),
|
||||||
take(1),
|
take(1),
|
||||||
map((results) => results.payload.totalElements > 0),
|
map((results) => results.payload.totalElements > 0),
|
||||||
);
|
);
|
||||||
@@ -320,8 +320,8 @@ export class CommunityListService {
|
|||||||
let hasChildren$: Observable<boolean>;
|
let hasChildren$: Observable<boolean>;
|
||||||
hasChildren$ = observableCombineLatest(hasSubcoms$, hasColls$).pipe(
|
hasChildren$ = observableCombineLatest(hasSubcoms$, hasColls$).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
map((result: [boolean]) => {
|
map(([hasSubcoms, hasColls]: [boolean, boolean]) => {
|
||||||
if (result[0] || result[1]) {
|
if (hasSubcoms || hasColls) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@@ -60,7 +60,8 @@ export class AuthService {
|
|||||||
// and is not the login route, clear redirect url and messages
|
// and is not the login route, clear redirect url and messages
|
||||||
const routeUrl$ = this.store.pipe(
|
const routeUrl$ = this.store.pipe(
|
||||||
select(routerStateSelector),
|
select(routerStateSelector),
|
||||||
filter((routerState: RouterReducerState) => isNotUndefined(routerState) && isNotUndefined(routerState.state)),
|
filter((routerState: RouterReducerState) => isNotUndefined(routerState)
|
||||||
|
&& isNotUndefined(routerState.state) && isNotEmpty(routerState.state.url)),
|
||||||
filter((routerState: RouterReducerState) => !this.isLoginRoute(routerState.state.url)),
|
filter((routerState: RouterReducerState) => !this.isLoginRoute(routerState.state.url)),
|
||||||
map((routerState: RouterReducerState) => routerState.state.url)
|
map((routerState: RouterReducerState) => routerState.state.url)
|
||||||
);
|
);
|
||||||
|
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { ActionReducerMap, } from '@ngrx/store';
|
||||||
ActionReducerMap,
|
|
||||||
createFeatureSelector,
|
|
||||||
} from '@ngrx/store';
|
|
||||||
|
|
||||||
import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer';
|
import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer';
|
||||||
import { indexReducer, MetaIndexState } from './index/index.reducer';
|
import { indexReducer, MetaIndexState } from './index/index.reducer';
|
||||||
@@ -9,17 +6,21 @@ import { requestReducer, RequestState } from './data/request.reducer';
|
|||||||
import { authReducer, AuthState } from './auth/auth.reducer';
|
import { authReducer, AuthState } from './auth/auth.reducer';
|
||||||
import { jsonPatchOperationsReducer, JsonPatchOperationsState } from './json-patch/json-patch-operations.reducer';
|
import { jsonPatchOperationsReducer, JsonPatchOperationsState } from './json-patch/json-patch-operations.reducer';
|
||||||
import { serverSyncBufferReducer, ServerSyncBufferState } from './cache/server-sync-buffer.reducer';
|
import { serverSyncBufferReducer, ServerSyncBufferState } from './cache/server-sync-buffer.reducer';
|
||||||
import {
|
import { objectUpdatesReducer, ObjectUpdatesState } from './data/object-updates/object-updates.reducer';
|
||||||
objectUpdatesReducer,
|
|
||||||
ObjectUpdatesState
|
|
||||||
} from './data/object-updates/object-updates.reducer';
|
|
||||||
import { routeReducer, RouteState } from './services/route.reducer';
|
import { routeReducer, RouteState } from './services/route.reducer';
|
||||||
|
import {
|
||||||
|
bitstreamFormatReducer,
|
||||||
|
BitstreamFormatRegistryState
|
||||||
|
} from '../+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
||||||
|
import { historyReducer, HistoryState } from './history/history.reducer';
|
||||||
|
|
||||||
export interface CoreState {
|
export interface CoreState {
|
||||||
|
'bitstreamFormats': BitstreamFormatRegistryState;
|
||||||
'cache/object': ObjectCacheState,
|
'cache/object': ObjectCacheState,
|
||||||
'cache/syncbuffer': ServerSyncBufferState,
|
'cache/syncbuffer': ServerSyncBufferState,
|
||||||
'cache/object-updates': ObjectUpdatesState
|
'cache/object-updates': ObjectUpdatesState
|
||||||
'data/request': RequestState,
|
'data/request': RequestState,
|
||||||
|
'history': HistoryState;
|
||||||
'index': MetaIndexState,
|
'index': MetaIndexState,
|
||||||
'auth': AuthState,
|
'auth': AuthState,
|
||||||
'json/patch': JsonPatchOperationsState,
|
'json/patch': JsonPatchOperationsState,
|
||||||
@@ -27,10 +28,12 @@ export interface CoreState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const coreReducers: ActionReducerMap<CoreState> = {
|
export const coreReducers: ActionReducerMap<CoreState> = {
|
||||||
|
'bitstreamFormats': bitstreamFormatReducer,
|
||||||
'cache/object': objectCacheReducer,
|
'cache/object': objectCacheReducer,
|
||||||
'cache/syncbuffer': serverSyncBufferReducer,
|
'cache/syncbuffer': serverSyncBufferReducer,
|
||||||
'cache/object-updates': objectUpdatesReducer,
|
'cache/object-updates': objectUpdatesReducer,
|
||||||
'data/request': requestReducer,
|
'data/request': requestReducer,
|
||||||
|
'history': historyReducer,
|
||||||
'index': indexReducer,
|
'index': indexReducer,
|
||||||
'auth': authReducer,
|
'auth': authReducer,
|
||||||
'json/patch': jsonPatchOperationsReducer,
|
'json/patch': jsonPatchOperationsReducer,
|
||||||
|
@@ -3,7 +3,6 @@ import { RequestEntry } from './request.reducer';
|
|||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { Action, Store } from '@ngrx/store';
|
import { Action, Store } from '@ngrx/store';
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
@@ -19,6 +18,7 @@ import {
|
|||||||
BitstreamFormatsRegistrySelectAction
|
BitstreamFormatsRegistrySelectAction
|
||||||
} from '../../+admin/admin-registries/bitstream-formats/bitstream-format.actions';
|
} from '../../+admin/admin-registries/bitstream-formats/bitstream-format.actions';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
|
||||||
describe('BitstreamFormatDataService', () => {
|
describe('BitstreamFormatDataService', () => {
|
||||||
let service: BitstreamFormatDataService;
|
let service: BitstreamFormatDataService;
|
||||||
|
@@ -5,7 +5,6 @@ import { RequestService } from './request.service';
|
|||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
import { createSelector, select, Store } from '@ngrx/store';
|
import { createSelector, select, Store } from '@ngrx/store';
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
@@ -17,7 +16,6 @@ import { find, map, tap } from 'rxjs/operators';
|
|||||||
import { configureRequest, getResponseFromEntry } from '../shared/operators';
|
import { configureRequest, getResponseFromEntry } from '../shared/operators';
|
||||||
import { distinctUntilChanged } from 'rxjs/internal/operators/distinctUntilChanged';
|
import { distinctUntilChanged } from 'rxjs/internal/operators/distinctUntilChanged';
|
||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { AppState } from '../../app.reducer';
|
|
||||||
import { BitstreamFormatRegistryState } from '../../+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
import { BitstreamFormatRegistryState } from '../../+admin/admin-registries/bitstream-formats/bitstream-format.reducers';
|
||||||
import {
|
import {
|
||||||
BitstreamFormatsRegistryDeselectAction,
|
BitstreamFormatsRegistryDeselectAction,
|
||||||
@@ -26,8 +24,9 @@ import {
|
|||||||
} from '../../+admin/admin-registries/bitstream-formats/bitstream-format.actions';
|
} from '../../+admin/admin-registries/bitstream-formats/bitstream-format.actions';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
|
||||||
const bitstreamFormatsStateSelector = (state: AppState) => state.bitstreamFormats;
|
const bitstreamFormatsStateSelector = (state: CoreState) => state.bitstreamFormats;
|
||||||
const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSelector,
|
const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSelector,
|
||||||
(bitstreamFormatRegistryState: BitstreamFormatRegistryState) => bitstreamFormatRegistryState.selectedBitstreamFormats);
|
(bitstreamFormatRegistryState: BitstreamFormatRegistryState) => bitstreamFormatRegistryState.selectedBitstreamFormats);
|
||||||
|
|
||||||
@@ -55,6 +54,7 @@ export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
|
|||||||
/**
|
/**
|
||||||
* Get the endpoint for browsing bitstream formats
|
* Get the endpoint for browsing bitstream formats
|
||||||
* @param {FindListOptions} options
|
* @param {FindListOptions} options
|
||||||
|
* @param {string} linkPath
|
||||||
* @returns {Observable<string>}
|
* @returns {Observable<string>}
|
||||||
*/
|
*/
|
||||||
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
||||||
@@ -99,7 +99,7 @@ export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new BitstreamFormat
|
* Create a new BitstreamFormat
|
||||||
* @param BitstreamFormat
|
* @param {BitstreamFormat} bitstreamFormat
|
||||||
*/
|
*/
|
||||||
public createBitstreamFormat(bitstreamFormat: BitstreamFormat): Observable<RestResponse> {
|
public createBitstreamFormat(bitstreamFormat: BitstreamFormat): Observable<RestResponse> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
@@ -1,32 +1,41 @@
|
|||||||
import {
|
import {
|
||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
filter, first,
|
filter, first,map, mergeMap, share, switchMap,
|
||||||
map,
|
|
||||||
mergeMap,
|
|
||||||
share,
|
|
||||||
switchMap,
|
|
||||||
take,
|
take,
|
||||||
tap
|
tap
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
|
import { merge as observableMerge, Observable, throwError as observableThrowError, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { CommunityDataService } from './community-data.service';
|
import { CommunityDataService } from './community-data.service';
|
||||||
|
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
|
import { DeleteRequest, FindListOptions, FindByIDRequest, RestRequest } from './request.models';
|
||||||
import { PaginatedList } from './paginated-list';
|
import { PaginatedList } from './paginated-list';
|
||||||
import { RemoteData } from './remote-data';
|
import { RemoteData } from './remote-data';
|
||||||
import { FindListOptions, FindByIDRequest } from './request.models';
|
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { getResponseFromEntry } from '../shared/operators';
|
import {
|
||||||
|
configureRequest,
|
||||||
|
getRemoteDataPayload,
|
||||||
|
getResponseFromEntry,
|
||||||
|
getSucceededRemoteData
|
||||||
|
} from '../shared/operators';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
import { Bitstream } from '../shared/bitstream.model';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
|
|
||||||
export abstract class ComColDataService<T extends CacheableObject> extends DataService<T> {
|
export abstract class ComColDataService<T extends CacheableObject> extends DataService<T> {
|
||||||
protected abstract cds: CommunityDataService;
|
protected abstract cds: CommunityDataService;
|
||||||
protected abstract objectCache: ObjectCacheService;
|
protected abstract objectCache: ObjectCacheService;
|
||||||
protected abstract halService: HALEndpointService;
|
protected abstract halService: HALEndpointService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linkpath of endpoint to delete the logo
|
||||||
|
*/
|
||||||
|
protected logoDeleteLinkpath = 'bitstreams';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the scoped endpoint URL by fetching the object with
|
* Get the scoped endpoint URL by fetching the object with
|
||||||
* the given scopeID and returning its HAL link with this
|
* the given scopeID and returning its HAL link with this
|
||||||
@@ -76,4 +85,33 @@ export abstract class ComColDataService<T extends CacheableObject> extends DataS
|
|||||||
return this.findList(href$, options);
|
return this.findList(href$, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the endpoint for the community or collection's logo
|
||||||
|
* @param id The community or collection's ID
|
||||||
|
*/
|
||||||
|
public getLogoEndpoint(id: string): Observable<string> {
|
||||||
|
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||||
|
switchMap((href: string) => this.halService.getEndpoint('logo', `${href}/${id}`))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the logo from the community or collection
|
||||||
|
* @param dso The object to delete the logo from
|
||||||
|
*/
|
||||||
|
public deleteLogo(dso: DSpaceObject): Observable<RestResponse> {
|
||||||
|
const logo$ = (dso as any).logo;
|
||||||
|
if (hasValue(logo$)) {
|
||||||
|
return observableCombineLatest(
|
||||||
|
logo$.pipe(getSucceededRemoteData(), getRemoteDataPayload(), take(1)),
|
||||||
|
this.halService.getEndpoint(this.logoDeleteLinkpath)
|
||||||
|
).pipe(
|
||||||
|
map(([logo, href]: [Bitstream, string]) => `${href}/${logo.id}`),
|
||||||
|
map((href: string) => new DeleteRequest(this.requestService.generateRequestId(), href)),
|
||||||
|
configureRequest(this.requestService),
|
||||||
|
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
||||||
|
getResponseFromEntry()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,22 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, find, first, map, mergeMap, skipWhile, switchMap, take, tap } from 'rxjs/operators';
|
import {
|
||||||
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
|
find,
|
||||||
|
first,
|
||||||
|
map,
|
||||||
|
mergeMap,
|
||||||
|
skipWhile,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
tap
|
||||||
|
} from 'rxjs/operators';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
import { PaginatedList } from './paginated-list';
|
import { PaginatedList } from './paginated-list';
|
||||||
@@ -14,9 +24,9 @@ import { RemoteData } from './remote-data';
|
|||||||
import {
|
import {
|
||||||
CreateRequest,
|
CreateRequest,
|
||||||
DeleteByIDRequest,
|
DeleteByIDRequest,
|
||||||
|
FindByIDRequest,
|
||||||
FindListOptions,
|
FindListOptions,
|
||||||
FindListRequest,
|
FindListRequest,
|
||||||
FindByIDRequest,
|
|
||||||
GetRequest
|
GetRequest
|
||||||
} from './request.models';
|
} from './request.models';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
@@ -37,6 +47,7 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec
|
|||||||
import { ChangeAnalyzer } from './change-analyzer';
|
import { ChangeAnalyzer } from './change-analyzer';
|
||||||
import { RestRequestMethod } from './rest-request-method';
|
import { RestRequestMethod } from './rest-request-method';
|
||||||
import { getMapsToType } from '../cache/builders/build-decorators';
|
import { getMapsToType } from '../cache/builders/build-decorators';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
|
||||||
export abstract class DataService<T extends CacheableObject> {
|
export abstract class DataService<T extends CacheableObject> {
|
||||||
protected abstract requestService: RequestService;
|
protected abstract requestService: RequestService;
|
||||||
|
@@ -6,22 +6,14 @@ import { CoreState } from '../core.reducers';
|
|||||||
import { ItemDataService } from './item-data.service';
|
import { ItemDataService } from './item-data.service';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import {
|
import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
|
||||||
DeleteRequest,
|
|
||||||
FindListOptions,
|
|
||||||
GetRequest,
|
|
||||||
MappedCollectionsRequest,
|
|
||||||
PostRequest,
|
|
||||||
RestRequest
|
|
||||||
} from './request.models';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
|
|
||||||
describe('ItemDataService', () => {
|
describe('ItemDataService', () => {
|
||||||
@@ -184,7 +176,7 @@ describe('ItemDataService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should configure a DELETE request', () => {
|
it('should configure a DELETE request', () => {
|
||||||
result.subscribe(() => expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(DeleteRequest), undefined));
|
result.subscribe(() => expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(DeleteRequest)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -198,7 +190,7 @@ describe('ItemDataService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should configure a POST request', () => {
|
it('should configure a POST request', () => {
|
||||||
result.subscribe(() => expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(PostRequest), undefined));
|
result.subscribe(() => expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(PostRequest)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { filter, find, map, switchMap, tap } from 'rxjs/operators';
|
import { filter, find, map, switchMap } from 'rxjs/operators';
|
||||||
import { configureRequest, getSucceededRemoteData } from '../shared/operators';
|
import { configureRequest, getSucceededRemoteData } from '../shared/operators';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||||
@@ -42,7 +42,7 @@ export class RelationshipTypeService {
|
|||||||
map((endpointURL: string) => new FindListRequest(this.requestService.generateRequestId(), endpointURL, options)),
|
map((endpointURL: string) => new FindListRequest(this.requestService.generateRequestId(), endpointURL, options)),
|
||||||
configureRequest(this.requestService),
|
configureRequest(this.requestService),
|
||||||
switchMap(() => this.rdbService.buildList(link$))
|
switchMap(() => this.rdbService.buildList(link$))
|
||||||
);
|
) as Observable<RemoteData<PaginatedList<RelationshipType>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,7 +2,6 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||||
import { AppState, keySelector } from '../../app.reducer';
|
import { AppState, keySelector } from '../../app.reducer';
|
||||||
@@ -13,24 +12,25 @@ import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-
|
|||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
||||||
import { SearchParam } from '../cache/models/search-param.model';
|
import { SearchParam } from '../cache/models/search-param.model';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
|
||||||
import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
|
import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
|
||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||||
|
import { RemoteData, RemoteDataState } from './remote-data';
|
||||||
|
import { PaginatedList } from './paginated-list';
|
||||||
|
import { ItemDataService } from './item-data.service';
|
||||||
import { Relationship } from '../shared/item-relationships/relationship.model';
|
import { Relationship } from '../shared/item-relationships/relationship.model';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||||
import { ItemDataService } from './item-data.service';
|
|
||||||
import { PaginatedList } from './paginated-list';
|
|
||||||
import { RemoteData, RemoteDataState } from './remote-data';
|
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../shared/metadata.models';
|
import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../shared/metadata.models';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
|
const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
getResponseFromEntry(),
|
getResponseFromEntry(),
|
||||||
tap(() => this.removeRelationshipItemsFromCache(item1)),
|
tap(() => this.removeRelationshipItemsFromCache(item1)),
|
||||||
tap(() => this.removeRelationshipItemsFromCache(item2))
|
tap(() => this.removeRelationshipItemsFromCache(item2))
|
||||||
);
|
) as Observable<RestResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,7 +357,7 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
* @param nameVariant The name variant to set for the matching relationship
|
* @param nameVariant The name variant to set for the matching relationship
|
||||||
*/
|
*/
|
||||||
public updateNameVariant(item1: Item, item2: Item, relationshipLabel: string, nameVariant: string): Observable<RemoteData<Relationship>> {
|
public updateNameVariant(item1: Item, item2: Item, relationshipLabel: string, nameVariant: string): Observable<RemoteData<Relationship>> {
|
||||||
const update$ = this.getRelationshipByItemsAndLabel(item1, item2, relationshipLabel)
|
const update$: Observable<RemoteData<Relationship>> = this.getRelationshipByItemsAndLabel(item1, item2, relationshipLabel)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((relation: Relationship) =>
|
switchMap((relation: Relationship) =>
|
||||||
relation.relationshipType.pipe(
|
relation.relationshipType.pipe(
|
||||||
@@ -391,6 +391,11 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
return update$
|
return update$
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to update the the right or left place of a relationship
|
||||||
|
* The useLeftItem field in the reorderable relationship determines which place should be updated
|
||||||
|
* @param reoRel
|
||||||
|
*/
|
||||||
public updatePlace(reoRel: ReorderableRelationship): Observable<RemoteData<Relationship>> {
|
public updatePlace(reoRel: ReorderableRelationship): Observable<RemoteData<Relationship>> {
|
||||||
let updatedRelationship;
|
let updatedRelationship;
|
||||||
if (reoRel.useLeftItem) {
|
if (reoRel.useLeftItem) {
|
||||||
|
@@ -21,6 +21,7 @@ import {
|
|||||||
} from './request.models';
|
} from './request.models';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { RequestEntry } from './request.reducer';
|
||||||
|
|
||||||
describe('RequestService', () => {
|
describe('RequestService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
@@ -107,7 +108,7 @@ describe('RequestService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getByHref').and.returnValue(observableOf({
|
spyOn(service, 'getByHref').and.returnValue(observableOf({
|
||||||
completed: false
|
completed: false
|
||||||
}))
|
} as RequestEntry))
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true', () => {
|
it('should return true', () => {
|
||||||
@@ -122,7 +123,7 @@ describe('RequestService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getByHref').and.returnValues(observableOf({
|
spyOn(service, 'getByHref').and.returnValues(observableOf({
|
||||||
completed: true
|
completed: true
|
||||||
}));
|
} as RequestEntry));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false', () => {
|
it('should return false', () => {
|
||||||
@@ -432,7 +433,7 @@ describe('RequestService', () => {
|
|||||||
let valid;
|
let valid;
|
||||||
const requestEntry = { completed: false };
|
const requestEntry = { completed: false };
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry));
|
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry as RequestEntry));
|
||||||
valid = serviceAsAny.isValid(requestEntry);
|
valid = serviceAsAny.isValid(requestEntry);
|
||||||
});
|
});
|
||||||
it('return an observable emitting false', () => {
|
it('return an observable emitting false', () => {
|
||||||
@@ -444,7 +445,7 @@ describe('RequestService', () => {
|
|||||||
let valid;
|
let valid;
|
||||||
const requestEntry = { completed: true, response: { isSuccessful: false } };
|
const requestEntry = { completed: true, response: { isSuccessful: false } };
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry));
|
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry as RequestEntry));
|
||||||
valid = serviceAsAny.isValid(requestEntry);
|
valid = serviceAsAny.isValid(requestEntry);
|
||||||
});
|
});
|
||||||
it('return an observable emitting false', () => {
|
it('return an observable emitting false', () => {
|
||||||
@@ -470,7 +471,7 @@ describe('RequestService', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(Date.prototype, 'getTime').and.returnValue(now);
|
spyOn(Date.prototype, 'getTime').and.returnValue(now);
|
||||||
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry));
|
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry as RequestEntry));
|
||||||
valid = serviceAsAny.isValid(requestEntry);
|
valid = serviceAsAny.isValid(requestEntry);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -497,7 +498,7 @@ describe('RequestService', () => {
|
|||||||
};
|
};
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(Date.prototype, 'getTime').and.returnValue(now);
|
spyOn(Date.prototype, 'getTime').and.returnValue(now);
|
||||||
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry));
|
spyOn(service, 'getByUUID').and.returnValue(observableOf(requestEntry as RequestEntry));
|
||||||
valid = serviceAsAny.isValid(requestEntry);
|
valid = serviceAsAny.isValid(requestEntry);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -5,8 +5,6 @@ import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
|||||||
import { Observable, race as observableRace } from 'rxjs';
|
import { Observable, race as observableRace } from 'rxjs';
|
||||||
import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||||
import { cloneDeep, remove } from 'lodash';
|
import { cloneDeep, remove } from 'lodash';
|
||||||
|
|
||||||
import { AppState } from '../../app.reducer';
|
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
@@ -19,7 +17,7 @@ import {
|
|||||||
} from '../index/index.selectors';
|
} from '../index/index.selectors';
|
||||||
import { UUIDService } from '../shared/uuid.service';
|
import { UUIDService } from '../shared/uuid.service';
|
||||||
import { RequestConfigureAction, RequestExecuteAction, RequestRemoveAction } from './request.actions';
|
import { RequestConfigureAction, RequestExecuteAction, RequestRemoveAction } from './request.actions';
|
||||||
import { GetRequest, RestRequest, SubmissionRequest } from './request.models';
|
import { GetRequest, RestRequest } from './request.models';
|
||||||
import { RequestEntry, RequestState } from './request.reducer';
|
import { RequestEntry, RequestState } from './request.reducer';
|
||||||
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
||||||
import { RestRequestMethod } from './rest-request-method';
|
import { RestRequestMethod } from './rest-request-method';
|
||||||
@@ -52,7 +50,7 @@ const entryFromUUIDSelector = (uuid: string): MemoizedSelector<CoreState, Reques
|
|||||||
* @param href Substring that the request's href should contain
|
* @param href Substring that the request's href should contain
|
||||||
*/
|
*/
|
||||||
const uuidsFromHrefSubstringSelector =
|
const uuidsFromHrefSubstringSelector =
|
||||||
(selector: MemoizedSelector<AppState, IndexState>, href: string): MemoizedSelector<AppState, string[]> => createSelector(
|
(selector: MemoizedSelector<CoreState, IndexState>, href: string): MemoizedSelector<CoreState, string[]> => createSelector(
|
||||||
selector,
|
selector,
|
||||||
(state: IndexState) => getUuidsFromHrefSubstring(state, href)
|
(state: IndexState) => getUuidsFromHrefSubstring(state, href)
|
||||||
);
|
);
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service';
|
||||||
|
|
||||||
|
describe('StatusCodeOnlyResponseParsingService', () => {
|
||||||
|
let service;
|
||||||
|
let statusCode;
|
||||||
|
let statusText;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new StatusCodeOnlyResponseParsingService();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parse', () => {
|
||||||
|
|
||||||
|
it('should return a RestResponse that doesn\'t contain the response body', () => {
|
||||||
|
const payload = 'd9128e44-183b-479d-aa2e-d39435838bf6';
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
payload,
|
||||||
|
statusCode: 201,
|
||||||
|
statusText: '201'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(JSON.stringify(result).indexOf(payload)).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the response is successful', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
statusCode = 201;
|
||||||
|
statusText = `${statusCode}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a success RestResponse', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.isSuccessful).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status code', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusCode).toBe(statusCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status text', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusText).toBe(statusText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the response is unsuccessful', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
statusCode = 400;
|
||||||
|
statusText = `${statusCode}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error RestResponse', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.isSuccessful).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status code', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusCode).toBe(statusCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with the correct status text', () => {
|
||||||
|
const result = service.parse(undefined, {
|
||||||
|
statusCode,
|
||||||
|
statusText
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.statusText).toBe(statusText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
|
import { ResponseParsingService } from './parsing.service';
|
||||||
|
import { RestRequest } from './request.models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A responseparser that will only look at the status code and status
|
||||||
|
* text of the response, and ignore anything else that might be there
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class StatusCodeOnlyResponseParsingService implements ResponseParsingService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the response and only extract the status code and status text
|
||||||
|
*
|
||||||
|
* @param request The request that was sent to the server
|
||||||
|
* @param data The response to parse
|
||||||
|
*/
|
||||||
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
|
const isSuccessful = data.statusCode >= 200 && data.statusCode < 300;
|
||||||
|
return new RestResponse(isSuccessful, data.statusCode, data.statusText);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
import { type } from '../ngrx/type';
|
import { type } from '../../shared/ngrx/type';
|
||||||
|
|
||||||
export const HistoryActionTypes = {
|
export const HistoryActionTypes = {
|
||||||
ADD_TO_HISTORY: type('dspace/history/ADD_TO_HISTORY'),
|
ADD_TO_HISTORY: type('dspace/history/ADD_TO_HISTORY'),
|
3
src/app/core/history/selectors.ts
Normal file
3
src/app/core/history/selectors.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
|
||||||
|
export const historySelector = (state: CoreState) => state.history;
|
@@ -1,5 +1,4 @@
|
|||||||
import { createSelector, MemoizedSelector } from '@ngrx/store';
|
import { createSelector, MemoizedSelector } from '@ngrx/store';
|
||||||
import { AppState } from '../../app.reducer';
|
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { coreSelector } from '../core.selectors';
|
import { coreSelector } from '../core.selectors';
|
||||||
@@ -11,7 +10,7 @@ import { IndexName, IndexState, MetaIndexState } from './index.reducer';
|
|||||||
* @returns
|
* @returns
|
||||||
* a MemoizedSelector to select the MetaIndexState
|
* a MemoizedSelector to select the MetaIndexState
|
||||||
*/
|
*/
|
||||||
export const metaIndexSelector: MemoizedSelector<AppState, MetaIndexState> = createSelector(
|
export const metaIndexSelector: MemoizedSelector<CoreState, MetaIndexState> = createSelector(
|
||||||
coreSelector,
|
coreSelector,
|
||||||
(state: CoreState) => state.index
|
(state: CoreState) => state.index
|
||||||
);
|
);
|
||||||
@@ -23,7 +22,7 @@ export const metaIndexSelector: MemoizedSelector<AppState, MetaIndexState> = cre
|
|||||||
* @returns
|
* @returns
|
||||||
* a MemoizedSelector to select the object index
|
* a MemoizedSelector to select the object index
|
||||||
*/
|
*/
|
||||||
export const objectIndexSelector: MemoizedSelector<AppState, IndexState> = createSelector(
|
export const objectIndexSelector: MemoizedSelector<CoreState, IndexState> = createSelector(
|
||||||
metaIndexSelector,
|
metaIndexSelector,
|
||||||
(state: MetaIndexState) => state[IndexName.OBJECT]
|
(state: MetaIndexState) => state[IndexName.OBJECT]
|
||||||
);
|
);
|
||||||
@@ -34,7 +33,7 @@ export const objectIndexSelector: MemoizedSelector<AppState, IndexState> = creat
|
|||||||
* @returns
|
* @returns
|
||||||
* a MemoizedSelector to select the request index
|
* a MemoizedSelector to select the request index
|
||||||
*/
|
*/
|
||||||
export const requestIndexSelector: MemoizedSelector<AppState, IndexState> = createSelector(
|
export const requestIndexSelector: MemoizedSelector<CoreState, IndexState> = createSelector(
|
||||||
metaIndexSelector,
|
metaIndexSelector,
|
||||||
(state: MetaIndexState) => state[IndexName.REQUEST]
|
(state: MetaIndexState) => state[IndexName.REQUEST]
|
||||||
);
|
);
|
||||||
@@ -45,7 +44,7 @@ export const requestIndexSelector: MemoizedSelector<AppState, IndexState> = crea
|
|||||||
* @returns
|
* @returns
|
||||||
* a MemoizedSelector to select the request UUID mapping
|
* a MemoizedSelector to select the request UUID mapping
|
||||||
*/
|
*/
|
||||||
export const requestUUIDIndexSelector: MemoizedSelector<AppState, IndexState> = createSelector(
|
export const requestUUIDIndexSelector: MemoizedSelector<CoreState, IndexState> = createSelector(
|
||||||
metaIndexSelector,
|
metaIndexSelector,
|
||||||
(state: MetaIndexState) => state[IndexName.UUID_MAPPING]
|
(state: MetaIndexState) => state[IndexName.UUID_MAPPING]
|
||||||
);
|
);
|
||||||
@@ -53,14 +52,13 @@ export const requestUUIDIndexSelector: MemoizedSelector<AppState, IndexState> =
|
|||||||
/**
|
/**
|
||||||
* Return the self link of an object in the object-cache based on its UUID
|
* Return the self link of an object in the object-cache based on its UUID
|
||||||
*
|
*
|
||||||
* @param id
|
* @param uuid
|
||||||
* the UUID for which you want to find the matching self link
|
* the UUID for which you want to find the matching self link
|
||||||
* @param identifierType the type of index, used to select index from state
|
|
||||||
* @returns
|
* @returns
|
||||||
* a MemoizedSelector to select the self link
|
* a MemoizedSelector to select the self link
|
||||||
*/
|
*/
|
||||||
export const selfLinkFromUuidSelector =
|
export const selfLinkFromUuidSelector =
|
||||||
(uuid: string): MemoizedSelector<AppState, string> => createSelector(
|
(uuid: string): MemoizedSelector<CoreState, string> => createSelector(
|
||||||
objectIndexSelector,
|
objectIndexSelector,
|
||||||
(state: IndexState) => hasValue(state) ? state[uuid] : undefined
|
(state: IndexState) => hasValue(state) ? state[uuid] : undefined
|
||||||
);
|
);
|
||||||
@@ -74,7 +72,7 @@ export const selfLinkFromUuidSelector =
|
|||||||
* a MemoizedSelector to select the UUID
|
* a MemoizedSelector to select the UUID
|
||||||
*/
|
*/
|
||||||
export const uuidFromHrefSelector =
|
export const uuidFromHrefSelector =
|
||||||
(href: string): MemoizedSelector<AppState, string> => createSelector(
|
(href: string): MemoizedSelector<CoreState, string> => createSelector(
|
||||||
requestIndexSelector,
|
requestIndexSelector,
|
||||||
(state: IndexState) => hasValue(state) ? state[href] : undefined
|
(state: IndexState) => hasValue(state) ? state[href] : undefined
|
||||||
);
|
);
|
||||||
@@ -89,7 +87,7 @@ export const uuidFromHrefSelector =
|
|||||||
* a MemoizedSelector to select the UUID of the cached request
|
* a MemoizedSelector to select the UUID of the cached request
|
||||||
*/
|
*/
|
||||||
export const originalRequestUUIDFromRequestUUIDSelector =
|
export const originalRequestUUIDFromRequestUUIDSelector =
|
||||||
(uuid: string): MemoizedSelector<AppState, string> => createSelector(
|
(uuid: string): MemoizedSelector<CoreState, string> => createSelector(
|
||||||
requestUUIDIndexSelector,
|
requestUUIDIndexSelector,
|
||||||
(state: IndexState) => hasValue(state) ? state[uuid] : undefined
|
(state: IndexState) => hasValue(state) ? state[uuid] : undefined
|
||||||
);
|
);
|
||||||
|
@@ -17,7 +17,6 @@ import {
|
|||||||
} from './json-patch-operations.actions';
|
} from './json-patch-operations.actions';
|
||||||
import { JsonPatchOperationModel } from './json-patch.model';
|
import { JsonPatchOperationModel } from './json-patch.model';
|
||||||
import { getResponseFromEntry } from '../shared/operators';
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
import { ObjectCacheEntry } from '../cache/object-cache.reducer';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class that provides methods to make JSON Patch requests.
|
* An abstract class that provides methods to make JSON Patch requests.
|
||||||
@@ -88,8 +87,8 @@ export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, Patch
|
|||||||
flatMap(() => {
|
flatMap(() => {
|
||||||
const [successResponse$, errorResponse$] = partition((response: RestResponse) => response.isSuccessful)(this.requestService.getByUUID(requestId).pipe(
|
const [successResponse$, errorResponse$] = partition((response: RestResponse) => response.isSuccessful)(this.requestService.getByUUID(requestId).pipe(
|
||||||
getResponseFromEntry(),
|
getResponseFromEntry(),
|
||||||
find((entry: ObjectCacheEntry) => startTransactionTime < entry.timeAdded),
|
find((entry: RestResponse) => startTransactionTime < entry.timeAdded),
|
||||||
map((entry: ObjectCacheEntry) => entry),
|
map((entry: RestResponse) => entry),
|
||||||
));
|
));
|
||||||
return observableMerge(
|
return observableMerge(
|
||||||
errorResponse$.pipe(
|
errorResponse$.pipe(
|
||||||
|
@@ -8,7 +8,7 @@ import { getTestScheduler, hot } from 'jasmine-marbles';
|
|||||||
import { RouteService } from './route.service';
|
import { RouteService } from './route.service';
|
||||||
import { MockRouter } from '../../shared/mocks/mock-router';
|
import { MockRouter } from '../../shared/mocks/mock-router';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { AddUrlToHistoryAction } from '../../shared/history/history.actions';
|
import { AddUrlToHistoryAction } from '../history/history.actions';
|
||||||
|
|
||||||
describe('RouteService', () => {
|
describe('RouteService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
|
@@ -1,28 +1,17 @@
|
|||||||
import { distinctUntilChanged, filter, map, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, take } from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import { ActivatedRoute, NavigationEnd, Params, Router, RouterStateSnapshot, } from '@angular/router';
|
||||||
ActivatedRoute,
|
|
||||||
NavigationEnd,
|
|
||||||
Params,
|
|
||||||
Router,
|
|
||||||
RouterStateSnapshot,
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { combineLatest, Observable } from 'rxjs';
|
import { combineLatest, Observable } from 'rxjs';
|
||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import {
|
import { AddParameterAction, SetParameterAction, SetParametersAction, SetQueryParametersAction } from './route.actions';
|
||||||
AddParameterAction,
|
import { CoreState } from '../core.reducers';
|
||||||
SetParameterAction,
|
import { coreSelector } from '../core.selectors';
|
||||||
SetParametersAction,
|
|
||||||
SetQueryParametersAction
|
|
||||||
} from './route.actions';
|
|
||||||
import { CoreState } from '../../core/core.reducers';
|
|
||||||
import { coreSelector } from '../../core/core.selectors';
|
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { historySelector } from '../../shared/history/selectors';
|
import { historySelector } from '../history/selectors';
|
||||||
import { AddUrlToHistoryAction } from '../../shared/history/history.actions';
|
import { AddUrlToHistoryAction } from '../history/history.actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selector to select all route parameters from the store
|
* Selector to select all route parameters from the store
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ul class="navbar-nav" [ngClass]="{'mr-auto': (isXsOrSm$ | async)}">
|
<ul class="navbar-nav" [ngClass]="{'mr-auto': (isXsOrSm$ | async)}">
|
||||||
<li *ngIf="!(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item" (click)="$event.stopPropagation();">
|
<li *ngIf="!(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item" (click)="$event.stopPropagation();">
|
||||||
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
|
<div ngbDropdown display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut>
|
||||||
<a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a>
|
<a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a>
|
||||||
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
|
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
|
||||||
<ds-log-in
|
<ds-log-in
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<a id="loginLink" routerLink="/login" routerLinkActive="active" class="px-1" >{{ 'nav.login' | translate }}<span class="sr-only">(current)</span></a>
|
<a id="loginLink" routerLink="/login" routerLinkActive="active" class="px-1" >{{ 'nav.login' | translate }}<span class="sr-only">(current)</span></a>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item">
|
<li *ngIf="(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item">
|
||||||
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
|
<div ngbDropdown display="dynamic" placement="bottom-right" class="d-inline-block" @fadeInOut>
|
||||||
<a href="#" id="dropdownUser" (click)="$event.preventDefault()" class="px-1" ngbDropdownToggle><i class="fas fa-user-circle fa-lg fa-fw" [title]="'nav.logout' | translate"></i></a>
|
<a href="#" id="dropdownUser" (click)="$event.preventDefault()" class="px-1" ngbDropdownToggle><i class="fas fa-user-circle fa-lg fa-fw" [title]="'nav.logout' | translate"></i></a>
|
||||||
<div id="logoutDropdownMenu" ngbDropdownMenu aria-labelledby="dropdownUser">
|
<div id="logoutDropdownMenu" ngbDropdownMenu aria-labelledby="dropdownUser">
|
||||||
<ds-user-menu></ds-user-menu>
|
<ds-user-menu></ds-user-menu>
|
||||||
|
@@ -1,3 +1,38 @@
|
|||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 d-inline-block">
|
||||||
|
<label>{{type.value + '.edit.logo.label' | translate}}</label>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngVar="(dso?.logo | async)?.payload as logo">
|
||||||
|
<div class="col-12 d-inline-block alert" [ngClass]="{'alert-danger': markLogoForDeletion}" id="logo-section">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8 d-inline-block">
|
||||||
|
<ds-comcol-page-logo [logo]="logo"></ds-comcol-page-logo>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 d-inline-block">
|
||||||
|
<div *ngIf="logo" class="btn-group btn-group-sm float-right" role="group">
|
||||||
|
<button *ngIf="!markLogoForDeletion" type="button" class="btn btn-danger" (click)="deleteLogo()">
|
||||||
|
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="markLogoForDeletion" type="button" class="btn btn-warning" (click)="undoDeleteLogo()">
|
||||||
|
<i class="fas fa-undo" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!logo" class="col-12 d-inline-block">
|
||||||
|
<ds-uploader *ngIf="initializedUploaderOptions | async"
|
||||||
|
[dropMsg]="type.value + '.edit.logo.upload'"
|
||||||
|
[dropOverDocumentMsg]="type.value + '.edit.logo.upload'"
|
||||||
|
[enableDragOverDocument]="true"
|
||||||
|
[uploadFilesOptions]="uploadFilesOptions"
|
||||||
|
(onCompleteItem)="onCompleteItem()"
|
||||||
|
(onUploadError)="onUploadError()"></ds-uploader>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<ds-form *ngIf="formModel"
|
<ds-form *ngIf="formModel"
|
||||||
[formId]="'comcol-form-id'"
|
[formId]="'comcol-form-id'"
|
||||||
[formModel]="formModel" (submitForm)="onSubmit()" (cancel)="onCancel()"></ds-form>
|
[formModel]="formModel" (submitForm)="onSubmit()" (cancel)="onCancel()"></ds-form>
|
||||||
|
@@ -3,14 +3,25 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { DynamicFormService, DynamicInputModel } from '@ng-dynamic-forms/core';
|
import { DynamicFormControlModel, DynamicFormService, DynamicInputModel } from '@ng-dynamic-forms/core';
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { ResourceType } from '../../../core/shared/resource-type';
|
|
||||||
import { ComColFormComponent } from './comcol-form.component';
|
import { ComColFormComponent } from './comcol-form.component';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { hasValue } from '../../empty.util';
|
import { hasValue } from '../../empty.util';
|
||||||
|
import { VarDirective } from '../../utils/var.directive';
|
||||||
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||||
|
import { AuthService } from '../../../core/auth/auth.service';
|
||||||
|
import { AuthServiceMock } from '../../mocks/mock-auth.service';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { RestRequestMethod } from '../../../core/data/rest-request-method';
|
||||||
|
import { ErrorResponse, RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { RequestError } from '../../../core/data/request.models';
|
||||||
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
describe('ComColFormComponent', () => {
|
describe('ComColFormComponent', () => {
|
||||||
let comp: ComColFormComponent<DSpaceObject>;
|
let comp: ComColFormComponent<DSpaceObject>;
|
||||||
@@ -49,71 +60,264 @@ describe('ComColFormComponent', () => {
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const logoEndpoint = 'rest/api/logo/endpoint';
|
||||||
|
const dsoService = Object.assign({
|
||||||
|
getLogoEndpoint: () => observableOf(logoEndpoint),
|
||||||
|
deleteLogo: () => observableOf({})
|
||||||
|
});
|
||||||
|
const notificationsService = new NotificationsServiceStub();
|
||||||
|
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
const locationStub = jasmine.createSpyObj('location', ['back']);
|
const locationStub = jasmine.createSpyObj('location', ['back']);
|
||||||
/* tslint:enable:no-empty */
|
/* tslint:enable:no-empty */
|
||||||
|
|
||||||
|
const requestServiceStub = jasmine.createSpyObj({
|
||||||
|
removeByHrefSubstring: {}
|
||||||
|
});
|
||||||
|
const objectCacheStub = jasmine.createSpyObj({
|
||||||
|
remove: {}
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
||||||
declarations: [ComColFormComponent],
|
declarations: [ComColFormComponent, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: Location, useValue: locationStub },
|
{ provide: Location, useValue: locationStub },
|
||||||
{ provide: DynamicFormService, useValue: formServiceStub }
|
{ provide: DynamicFormService, useValue: formServiceStub },
|
||||||
|
{ provide: NotificationsService, useValue: notificationsService },
|
||||||
|
{ provide: AuthService, useValue: new AuthServiceMock() },
|
||||||
|
{ provide: RequestService, useValue: requestServiceStub },
|
||||||
|
{ provide: ObjectCacheService, useValue: objectCacheStub }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
describe('when the dso doesn\'t contain an ID (newly created)', () => {
|
||||||
fixture = TestBed.createComponent(ComColFormComponent);
|
|
||||||
comp = fixture.componentInstance;
|
|
||||||
comp.formModel = [];
|
|
||||||
comp.dso = new Community();
|
|
||||||
fixture.detectChanges();
|
|
||||||
location = (comp as any).location;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onSubmit', () => {
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(comp.submitForm, 'emit');
|
initComponent(new Community());
|
||||||
comp.formModel = formModel;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit the new version of the community', () => {
|
it('should initialize the uploadFilesOptions with a placeholder url', () => {
|
||||||
comp.dso = Object.assign(
|
expect(comp.uploadFilesOptions.url.length).toBeGreaterThan(0);
|
||||||
new Community(),
|
});
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
...titleMD,
|
|
||||||
...randomMD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
comp.onSubmit();
|
describe('onSubmit', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(comp.submitForm, 'emit');
|
||||||
|
comp.formModel = formModel;
|
||||||
|
});
|
||||||
|
|
||||||
expect(comp.submitForm.emit).toHaveBeenCalledWith(
|
it('should emit the new version of the community', () => {
|
||||||
Object.assign(
|
comp.dso = Object.assign(
|
||||||
{},
|
|
||||||
new Community(),
|
new Community(),
|
||||||
{
|
{
|
||||||
metadata: {
|
metadata: {
|
||||||
...newTitleMD,
|
...titleMD,
|
||||||
...randomMD,
|
...randomMD
|
||||||
...abstractMD
|
}
|
||||||
},
|
}
|
||||||
type: Community.type
|
);
|
||||||
},
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onCancel', () => {
|
comp.onSubmit();
|
||||||
it('should call the back method on the Location service', () => {
|
|
||||||
|
expect(comp.submitForm.emit).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
dso: Object.assign(
|
||||||
|
{},
|
||||||
|
new Community(),
|
||||||
|
{
|
||||||
|
metadata: {
|
||||||
|
...newTitleMD,
|
||||||
|
...randomMD,
|
||||||
|
...abstractMD
|
||||||
|
},
|
||||||
|
type: Community.type
|
||||||
|
},
|
||||||
|
),
|
||||||
|
uploader: {} as any,
|
||||||
|
deleteLogo: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onCancel', () => {
|
||||||
|
it('should call the back method on the Location service', () => {
|
||||||
comp.onCancel();
|
comp.onCancel();
|
||||||
expect(locationStub.back).toHaveBeenCalled();
|
expect(locationStub.back).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onCompleteItem', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(comp.finish, 'emit');
|
||||||
|
comp.onCompleteItem();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show a success notification', () => {
|
||||||
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit finish', () => {
|
||||||
|
expect(comp.finish.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove the object\'s cache', () => {
|
||||||
|
expect(requestServiceStub.removeByHrefSubstring).toHaveBeenCalled();
|
||||||
|
expect(objectCacheStub.remove).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onUploadError', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(comp.finish, 'emit');
|
||||||
|
comp.onUploadError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show an error notification', () => {
|
||||||
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit finish', () => {
|
||||||
|
expect(comp.finish.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the dso contains an ID (being edited)', () => {
|
||||||
|
describe('and the dso doesn\'t contain a logo', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
initComponent(Object.assign(new Community(), {
|
||||||
|
id: 'community-id',
|
||||||
|
logo: observableOf(new RemoteData(false, false, true, null, undefined))
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize the uploadFilesOptions with the logo\'s endpoint url', () => {
|
||||||
|
expect(comp.uploadFilesOptions.url).toEqual(logoEndpoint);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize the uploadFilesOptions with a POST method', () => {
|
||||||
|
expect(comp.uploadFilesOptions.method).toEqual(RestRequestMethod.POST);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and the dso contains a logo', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
initComponent(Object.assign(new Community(), {
|
||||||
|
id: 'community-id',
|
||||||
|
logo: observableOf(new RemoteData(false, false, true, null, {}))
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize the uploadFilesOptions with the logo\'s endpoint url', () => {
|
||||||
|
expect(comp.uploadFilesOptions.url).toEqual(logoEndpoint);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize the uploadFilesOptions with a PUT method', () => {
|
||||||
|
expect(comp.uploadFilesOptions.method).toEqual(RestRequestMethod.PUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('submit with logo marked for deletion', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.markLogoForDeletion = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when dsoService.deleteLogo returns a successful response', () => {
|
||||||
|
const response = new RestResponse(true, 200, 'OK');
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(dsoService, 'deleteLogo').and.returnValue(observableOf(response));
|
||||||
|
comp.onSubmit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display a success notification', () => {
|
||||||
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when dsoService.deleteLogo returns an error response', () => {
|
||||||
|
const response = new ErrorResponse(new RequestError('errorMessage'));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(dsoService, 'deleteLogo').and.returnValue(observableOf(response));
|
||||||
|
comp.onSubmit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an error notification', () => {
|
||||||
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteLogo', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.deleteLogo();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set markLogoForDeletion to true', () => {
|
||||||
|
expect(comp.markLogoForDeletion).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mark the logo section with a danger alert', () => {
|
||||||
|
const logoSection = fixture.debugElement.query(By.css('#logo-section.alert-danger'));
|
||||||
|
expect(logoSection).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide the delete button', () => {
|
||||||
|
const button = fixture.debugElement.query(By.css('#logo-section .btn-danger'));
|
||||||
|
expect(button).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the undo button', () => {
|
||||||
|
const button = fixture.debugElement.query(By.css('#logo-section .btn-warning'));
|
||||||
|
expect(button).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('undoDeleteLogo', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.markLogoForDeletion = true;
|
||||||
|
comp.undoDeleteLogo();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set markLogoForDeletion to false', () => {
|
||||||
|
expect(comp.markLogoForDeletion).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable the danger alert on the logo section', () => {
|
||||||
|
const logoSection = fixture.debugElement.query(By.css('#logo-section.alert-danger'));
|
||||||
|
expect(logoSection).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the delete button', () => {
|
||||||
|
const button = fixture.debugElement.query(By.css('#logo-section .btn-danger'));
|
||||||
|
expect(button).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide the undo button', () => {
|
||||||
|
const button = fixture.debugElement.query(By.css('#logo-section .btn-warning'));
|
||||||
|
expect(button).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function initComponent(dso: Community) {
|
||||||
|
fixture = TestBed.createComponent(ComColFormComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
comp.formModel = [];
|
||||||
|
comp.dso = dso;
|
||||||
|
(comp as any).type = Community.type;
|
||||||
|
comp.uploaderComponent = Object.assign({
|
||||||
|
uploader: {}
|
||||||
|
});
|
||||||
|
(comp as any).dsoService = dsoService;
|
||||||
|
fixture.detectChanges();
|
||||||
|
location = (comp as any).location;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,17 +1,34 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
|
DynamicFormControlModel,
|
||||||
DynamicFormService,
|
DynamicFormService,
|
||||||
DynamicInputModel
|
DynamicInputModel
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.models';
|
import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.models';
|
||||||
import { ResourceType } from '../../../core/shared/resource-type';
|
import { ResourceType } from '../../../core/shared/resource-type';
|
||||||
import { isNotEmpty } from '../../empty.util';
|
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||||
|
import { UploaderOptions } from '../../uploader/uploader-options.model';
|
||||||
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
|
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { AuthService } from '../../../core/auth/auth.service';
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
|
import { UploaderComponent } from '../../uploader/uploader.component';
|
||||||
|
import { FileUploader } from 'ng2-file-upload';
|
||||||
|
import { ErrorResponse, RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||||
|
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
|
import { RestRequestMethod } from '../../../core/data/rest-request-method';
|
||||||
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A form for creating and editing Communities or Collections
|
* A form for creating and editing Communities or Collections
|
||||||
@@ -21,7 +38,13 @@ import { Community } from '../../../core/shared/community.model';
|
|||||||
styleUrls: ['./comcol-form.component.scss'],
|
styleUrls: ['./comcol-form.component.scss'],
|
||||||
templateUrl: './comcol-form.component.html'
|
templateUrl: './comcol-form.component.html'
|
||||||
})
|
})
|
||||||
export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
export class ComColFormComponent<T extends DSpaceObject> implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logo uploader component
|
||||||
|
*/
|
||||||
|
@ViewChild(UploaderComponent) uploaderComponent: UploaderComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DSpaceObject that the form represents
|
* DSpaceObject that the form represents
|
||||||
*/
|
*/
|
||||||
@@ -30,7 +53,7 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Type of DSpaceObject that the form represents
|
* Type of DSpaceObject that the form represents
|
||||||
*/
|
*/
|
||||||
protected type: ResourceType;
|
type: ResourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {string} Key prefix used to generate form labels
|
* @type {string} Key prefix used to generate form labels
|
||||||
@@ -53,14 +76,56 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
formGroup: FormGroup;
|
formGroup: FormGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits DSO when the form is submitted
|
* The uploader configuration options
|
||||||
* @type {EventEmitter<any>}
|
* @type {UploaderOptions}
|
||||||
*/
|
*/
|
||||||
@Output() submitForm: EventEmitter<any> = new EventEmitter();
|
uploadFilesOptions: UploaderOptions = Object.assign(new UploaderOptions(), {
|
||||||
|
autoUpload: false
|
||||||
|
});
|
||||||
|
|
||||||
public constructor(private location: Location,
|
/**
|
||||||
private formService: DynamicFormService,
|
* Emits DSO and Uploader when the form is submitted
|
||||||
private translate: TranslateService) {
|
*/
|
||||||
|
@Output() submitForm: EventEmitter<{
|
||||||
|
dso: T,
|
||||||
|
uploader: FileUploader,
|
||||||
|
deleteLogo: boolean
|
||||||
|
}> = new EventEmitter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an event when the logo has finished uploading (with or without errors) or was removed
|
||||||
|
*/
|
||||||
|
@Output() finish: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable keeping track whether or not the uploader has finished initializing
|
||||||
|
* Used to start rendering the uploader component
|
||||||
|
*/
|
||||||
|
initializedUploaderOptions = new BehaviorSubject(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the logo marked to be deleted?
|
||||||
|
*/
|
||||||
|
markLogoForDeletion = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
protected subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service used to fetch from or send data to
|
||||||
|
*/
|
||||||
|
protected dsoService: ComColDataService<Community | Collection>;
|
||||||
|
|
||||||
|
public constructor(protected location: Location,
|
||||||
|
protected formService: DynamicFormService,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected objectCache: ObjectCacheService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -76,13 +141,56 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.updateFieldTranslations();
|
this.updateFieldTranslations();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (hasValue(this.dso.id)) {
|
||||||
|
this.subs.push(
|
||||||
|
observableCombineLatest(
|
||||||
|
this.dsoService.getLogoEndpoint(this.dso.id),
|
||||||
|
(this.dso as any).logo
|
||||||
|
).subscribe(([href, logoRD]: [string, RemoteData<Bitstream>]) => {
|
||||||
|
this.uploadFilesOptions.url = href;
|
||||||
|
this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
|
||||||
|
// If the object already contains a logo, send out a PUT request instead of POST for setting a new logo
|
||||||
|
if (hasValue(logoRD.payload)) {
|
||||||
|
this.uploadFilesOptions.method = RestRequestMethod.PUT;
|
||||||
|
}
|
||||||
|
this.initializedUploaderOptions.next(true);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Set a placeholder URL to not break the uploader component. This will be replaced once the object is created.
|
||||||
|
this.uploadFilesOptions.url = 'placeholder';
|
||||||
|
this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
|
||||||
|
this.initializedUploaderOptions.next(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks which new fields were added and sends the updated version of the DSO to the parent component
|
* Checks which new fields were added and sends the updated version of the DSO to the parent component
|
||||||
*/
|
*/
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
const formMetadata = new Object() as MetadataMap;
|
if (this.markLogoForDeletion && hasValue(this.dso.id)) {
|
||||||
|
this.dsoService.deleteLogo(this.dso).subscribe((response: RestResponse) => {
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
this.notificationsService.success(
|
||||||
|
this.translate.get(this.type.value + '.edit.logo.notifications.delete.success.title'),
|
||||||
|
this.translate.get(this.type.value + '.edit.logo.notifications.delete.success.content')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const errorResponse = response as ErrorResponse;
|
||||||
|
this.notificationsService.error(
|
||||||
|
this.translate.get(this.type.value + '.edit.logo.notifications.delete.error.title'),
|
||||||
|
errorResponse.errorMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(this.dso as any).logo = undefined;
|
||||||
|
this.uploadFilesOptions.method = RestRequestMethod.POST;
|
||||||
|
this.refreshCache();
|
||||||
|
this.finish.emit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const formMetadata = {} as MetadataMap;
|
||||||
this.formModel.forEach((fieldModel: DynamicInputModel) => {
|
this.formModel.forEach((fieldModel: DynamicInputModel) => {
|
||||||
const value: MetadataValue = {
|
const value: MetadataValue = {
|
||||||
value: fieldModel.value as string,
|
value: fieldModel.value as string,
|
||||||
@@ -102,7 +210,11 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
},
|
},
|
||||||
type: Community.type
|
type: Community.type
|
||||||
});
|
});
|
||||||
this.submitForm.emit(updatedDSO);
|
this.submitForm.emit({
|
||||||
|
dso: updatedDSO,
|
||||||
|
uploader: hasValue(this.uploaderComponent) ? this.uploaderComponent.uploader : undefined,
|
||||||
|
deleteLogo: this.markLogoForDeletion
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,7 +234,59 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the logo to be deleted
|
||||||
|
* Send out a delete request to remove the logo from the community/collection and display notifications
|
||||||
|
*/
|
||||||
|
deleteLogo() {
|
||||||
|
this.markLogoForDeletion = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo marking the logo to be deleted
|
||||||
|
*/
|
||||||
|
undoDeleteLogo() {
|
||||||
|
this.markLogoForDeletion = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the object's cache to ensure the latest version
|
||||||
|
*/
|
||||||
|
private refreshCache() {
|
||||||
|
this.requestService.removeByHrefSubstring(this.dso.self);
|
||||||
|
this.objectCache.remove(this.dso.self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request was successful, display a success notification
|
||||||
|
*/
|
||||||
|
public onCompleteItem() {
|
||||||
|
this.refreshCache();
|
||||||
|
this.notificationsService.success(null, this.translate.get(this.type.value + '.edit.logo.notifications.add.success'));
|
||||||
|
this.finish.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request was unsuccessful, display an error notification
|
||||||
|
*/
|
||||||
|
public onUploadError() {
|
||||||
|
this.notificationsService.error(null, this.translate.get(this.type.value + '.edit.logo.notifications.add.error'));
|
||||||
|
this.finish.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the form and return to the previous page
|
||||||
|
*/
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from open subscriptions
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs
|
||||||
|
.filter((subscription) => hasValue(subscription))
|
||||||
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,11 +11,13 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { CreateComColPageComponent } from './create-comcol-page.component';
|
import { CreateComColPageComponent } from './create-comcol-page.component';
|
||||||
import { DataService } from '../../../core/data/data.service';
|
|
||||||
import {
|
import {
|
||||||
createFailedRemoteDataObject$,
|
createFailedRemoteDataObject$,
|
||||||
createSuccessfulRemoteDataObject$
|
createSuccessfulRemoteDataObject$
|
||||||
} from '../../testing/utils';
|
} from '../../testing/utils';
|
||||||
|
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
||||||
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||||
|
|
||||||
describe('CreateComColPageComponent', () => {
|
describe('CreateComColPageComponent', () => {
|
||||||
let comp: CreateComColPageComponent<DSpaceObject>;
|
let comp: CreateComColPageComponent<DSpaceObject>;
|
||||||
@@ -31,6 +33,8 @@ describe('CreateComColPageComponent', () => {
|
|||||||
let routeServiceStub;
|
let routeServiceStub;
|
||||||
let routerStub;
|
let routerStub;
|
||||||
|
|
||||||
|
const logoEndpoint = 'rest/api/logo/endpoint';
|
||||||
|
|
||||||
function initializeVars() {
|
function initializeVars() {
|
||||||
community = Object.assign(new Community(), {
|
community = Object.assign(new Community(), {
|
||||||
uuid: 'a20da287-e174-466a-9926-f66b9300d347',
|
uuid: 'a20da287-e174-466a-9926-f66b9300d347',
|
||||||
@@ -56,8 +60,8 @@ describe('CreateComColPageComponent', () => {
|
|||||||
value: community.name
|
value: community.name
|
||||||
}]
|
}]
|
||||||
})),
|
})),
|
||||||
create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity)
|
create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity),
|
||||||
|
getLogoEndpoint: () => observableOf(logoEndpoint)
|
||||||
};
|
};
|
||||||
|
|
||||||
routeServiceStub = {
|
routeServiceStub = {
|
||||||
@@ -74,10 +78,11 @@ describe('CreateComColPageComponent', () => {
|
|||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: DataService, useValue: communityDataServiceStub },
|
{ provide: ComColDataService, useValue: communityDataServiceStub },
|
||||||
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -86,6 +91,7 @@ describe('CreateComColPageComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(CreateComColPageComponent);
|
fixture = TestBed.createComponent(CreateComColPageComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
|
(comp as any).type = Community.type;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
dsoDataService = (comp as any).dsoDataService;
|
dsoDataService = (comp as any).dsoDataService;
|
||||||
communityDataService = (comp as any).communityDataService;
|
communityDataService = (comp as any).communityDataService;
|
||||||
@@ -95,27 +101,86 @@ describe('CreateComColPageComponent', () => {
|
|||||||
|
|
||||||
describe('onSubmit', () => {
|
describe('onSubmit', () => {
|
||||||
let data;
|
let data;
|
||||||
beforeEach(() => {
|
|
||||||
data = Object.assign(new Community(), {
|
describe('with an empty queue in the uploader', () => {
|
||||||
metadata: [{
|
beforeEach(() => {
|
||||||
key: 'dc.title',
|
data = {
|
||||||
value: 'test'
|
dso: Object.assign(new Community(), {
|
||||||
}]
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'test'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
uploader: {
|
||||||
|
options: {
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
queue: [],
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
uploadAll: () => {}
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate when successful', () => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not navigate on failure', () => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity));
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should navigate when successful', () => {
|
|
||||||
spyOn(router, 'navigate');
|
|
||||||
comp.onSubmit(data);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(router.navigate).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not navigate on failure', () => {
|
describe('with at least one item in the uploader\'s queue', () => {
|
||||||
spyOn(router, 'navigate');
|
beforeEach(() => {
|
||||||
spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity));
|
data = {
|
||||||
comp.onSubmit(data);
|
dso: Object.assign(new Community(), {
|
||||||
fixture.detectChanges();
|
metadata: [{
|
||||||
expect(router.navigate).not.toHaveBeenCalled();
|
key: 'dc.title',
|
||||||
|
value: 'test'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
uploader: {
|
||||||
|
options: {
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
queue: [
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
uploadAll: () => {}
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not navigate', () => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the uploader\'s url to the logo\'s endpoint', () => {
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(data.uploader.options.url).toEqual(logoEndpoint);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the uploader\'s uploadAll', () => {
|
||||||
|
spyOn(data.uploader, 'uploadAll');
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(data.uploader.uploadAll).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -3,13 +3,17 @@ import { Community } from '../../../core/shared/community.model';
|
|||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RouteService } from '../../../core/services/route.service';
|
import { RouteService } from '../../../core/services/route.service';
|
||||||
import { Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { isNotEmpty, isNotUndefined } from '../../empty.util';
|
import { hasValue, isNotEmpty, isNotUndefined } from '../../empty.util';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { DataService } from '../../../core/data/data.service';
|
import { DataService } from '../../../core/data/data.service';
|
||||||
|
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
||||||
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ResourceType } from '../../../core/shared/resource-type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing the create page for communities and collections
|
* Component representing the create page for communities and collections
|
||||||
@@ -34,11 +38,23 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
*/
|
*/
|
||||||
public parentRD$: Observable<RemoteData<Community>>;
|
public parentRD$: Observable<RemoteData<Community>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UUID of the newly created object
|
||||||
|
*/
|
||||||
|
private newUUID: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the dso
|
||||||
|
*/
|
||||||
|
protected type: ResourceType;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected dsoDataService: DataService<TDomain>,
|
protected dsoDataService: ComColDataService<TDomain>,
|
||||||
protected parentDataService: CommunityDataService,
|
protected parentDataService: CommunityDataService,
|
||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router
|
protected router: Router,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -53,20 +69,40 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TDomain} dso The updated version of the DSO
|
|
||||||
* Creates a new DSO based on the submitted user data and navigates to the new object's home page
|
* Creates a new DSO based on the submitted user data and navigates to the new object's home page
|
||||||
|
* @param event The event returned by the community/collection form. Contains the new dso and logo uploader
|
||||||
*/
|
*/
|
||||||
onSubmit(dso: TDomain) {
|
onSubmit(event) {
|
||||||
|
const dso = event.dso;
|
||||||
|
const uploader = event.uploader;
|
||||||
|
|
||||||
this.parentUUID$.pipe(take(1)).subscribe((uuid: string) => {
|
this.parentUUID$.pipe(take(1)).subscribe((uuid: string) => {
|
||||||
this.dsoDataService.create(dso, uuid)
|
this.dsoDataService.create(dso, uuid)
|
||||||
.pipe(getSucceededRemoteData())
|
.pipe(getSucceededRemoteData())
|
||||||
.subscribe((dsoRD: RemoteData<TDomain>) => {
|
.subscribe((dsoRD: RemoteData<TDomain>) => {
|
||||||
if (isNotUndefined(dsoRD)) {
|
if (isNotUndefined(dsoRD)) {
|
||||||
const newUUID = dsoRD.payload.uuid;
|
this.newUUID = dsoRD.payload.uuid;
|
||||||
this.router.navigate([this.frontendURL + newUUID]);
|
if (uploader.queue.length > 0) {
|
||||||
|
this.dsoDataService.getLogoEndpoint(this.newUUID).pipe(take(1)).subscribe((href: string) => {
|
||||||
|
uploader.options.url = href;
|
||||||
|
uploader.uploadAll();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.navigateToNewPage();
|
||||||
|
}
|
||||||
|
this.notificationsService.success(null, this.translate.get(this.type.value + '.create.notifications.success'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the page of the newly created object
|
||||||
|
*/
|
||||||
|
navigateToNewPage() {
|
||||||
|
if (hasValue(this.newUUID)) {
|
||||||
|
this.router.navigate([this.frontendURL + this.newUUID]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,189 @@
|
|||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { CommunityDataService } from '../../../../core/data/community-data.service';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Community } from '../../../../core/shared/community.model';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { SharedModule } from '../../../shared.module';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { DataService } from '../../../../core/data/data.service';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComcolMetadataComponent } from './comcol-metadata.component';
|
||||||
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../testing/utils';
|
||||||
|
import { ComColDataService } from '../../../../core/data/comcol-data.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../testing/notifications-service-stub';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
|
||||||
|
describe('ComColMetadataComponent', () => {
|
||||||
|
let comp: ComcolMetadataComponent<DSpaceObject>;
|
||||||
|
let fixture: ComponentFixture<ComcolMetadataComponent<DSpaceObject>>;
|
||||||
|
let dsoDataService: CommunityDataService;
|
||||||
|
let router: Router;
|
||||||
|
|
||||||
|
let community;
|
||||||
|
let newCommunity;
|
||||||
|
let communityDataServiceStub;
|
||||||
|
let routerStub;
|
||||||
|
let routeStub;
|
||||||
|
|
||||||
|
const logoEndpoint = 'rest/api/logo/endpoint';
|
||||||
|
|
||||||
|
function initializeVars() {
|
||||||
|
community = Object.assign(new Community(), {
|
||||||
|
uuid: 'a20da287-e174-466a-9926-f66b9300d347',
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'test community'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
newCommunity = Object.assign(new Community(), {
|
||||||
|
uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48',
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'new community'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
communityDataServiceStub = {
|
||||||
|
update: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity),
|
||||||
|
getLogoEndpoint: () => observableOf(logoEndpoint)
|
||||||
|
};
|
||||||
|
|
||||||
|
routerStub = {
|
||||||
|
navigate: (commands) => commands
|
||||||
|
};
|
||||||
|
|
||||||
|
routeStub = {
|
||||||
|
parent: {
|
||||||
|
data: observableOf({
|
||||||
|
dso: new RemoteData(false, false, true, null, community)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
initializeVars();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: ComColDataService, useValue: communityDataServiceStub },
|
||||||
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ComcolMetadataComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
(comp as any).type = Community.type;
|
||||||
|
fixture.detectChanges();
|
||||||
|
dsoDataService = (comp as any).dsoDataService;
|
||||||
|
router = (comp as any).router;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onSubmit', () => {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
describe('with an empty queue in the uploader', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
dso: Object.assign(new Community(), {
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'test'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
uploader: {
|
||||||
|
options: {
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
queue: [],
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
uploadAll: () => {}
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate when successful', () => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not navigate on failure', () => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
spyOn(dsoDataService, 'update').and.returnValue(createFailedRemoteDataObject$(newCommunity));
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with at least one item in the uploader\'s queue', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
dso: Object.assign(new Community(), {
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'test'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
uploader: {
|
||||||
|
options: {
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
queue: [
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
uploadAll: () => {}
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not navigate', () => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the uploader\'s url to the logo\'s endpoint', () => {
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(data.uploader.options.url).toEqual(logoEndpoint);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the uploader\'s uploadAll', () => {
|
||||||
|
spyOn(data.uploader, 'uploadAll');
|
||||||
|
comp.onSubmit(data);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(data.uploader.uploadAll).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('navigateToHomePage', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
comp.navigateToHomePage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate', () => {
|
||||||
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,85 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { first, map, take } from 'rxjs/operators';
|
||||||
|
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
|
import { hasValue, isNotUndefined } from '../../../empty.util';
|
||||||
|
import { DataService } from '../../../../core/data/data.service';
|
||||||
|
import { ResourceType } from '../../../../core/shared/resource-type';
|
||||||
|
import { ComColDataService } from '../../../../core/data/comcol-data.service';
|
||||||
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-comcol-metadata',
|
||||||
|
template: ''
|
||||||
|
})
|
||||||
|
export class ComcolMetadataComponent<TDomain extends DSpaceObject> implements OnInit {
|
||||||
|
/**
|
||||||
|
* Frontend endpoint for this type of DSO
|
||||||
|
*/
|
||||||
|
protected frontendURL: string;
|
||||||
|
/**
|
||||||
|
* The initial DSO object
|
||||||
|
*/
|
||||||
|
public dsoRD$: Observable<RemoteData<TDomain>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the dso
|
||||||
|
*/
|
||||||
|
protected type: ResourceType;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
protected dsoDataService: ComColDataService<TDomain>,
|
||||||
|
protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected translate: TranslateService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing DSO based on the submitted user data and navigates to the edited object's home page
|
||||||
|
* @param event The event returned by the community/collection form. Contains the new dso and logo uploader
|
||||||
|
*/
|
||||||
|
onSubmit(event) {
|
||||||
|
const dso = event.dso;
|
||||||
|
const uploader = event.uploader;
|
||||||
|
const deleteLogo = event.deleteLogo;
|
||||||
|
|
||||||
|
this.dsoDataService.update(dso)
|
||||||
|
.pipe(getSucceededRemoteData())
|
||||||
|
.subscribe((dsoRD: RemoteData<TDomain>) => {
|
||||||
|
if (isNotUndefined(dsoRD)) {
|
||||||
|
const newUUID = dsoRD.payload.uuid;
|
||||||
|
if (hasValue(uploader) && uploader.queue.length > 0) {
|
||||||
|
this.dsoDataService.getLogoEndpoint(newUUID).pipe(take(1)).subscribe((href: string) => {
|
||||||
|
uploader.options.url = href;
|
||||||
|
uploader.uploadAll();
|
||||||
|
});
|
||||||
|
} else if (!deleteLogo) {
|
||||||
|
this.router.navigate([this.frontendURL + newUUID]);
|
||||||
|
}
|
||||||
|
this.notificationsService.success(null, this.translate.get(this.type.value + '.edit.notifications.success'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the home page of the object
|
||||||
|
*/
|
||||||
|
navigateToHomePage() {
|
||||||
|
this.dsoRD$.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
take(1)
|
||||||
|
).subscribe((dsoRD: RemoteData<TDomain>) => {
|
||||||
|
this.router.navigate([this.frontendURL + dsoRD.payload.id]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2 class="border-bottom">{{ type + '.edit.head' | translate }}</h2>
|
||||||
|
<div class="pt-2">
|
||||||
|
<ul class="nav nav-tabs justify-content-start mb-2">
|
||||||
|
<li *ngFor="let page of pages" class="nav-item">
|
||||||
|
<a class="nav-link"
|
||||||
|
[ngClass]="{'active' : page === currentPage}"
|
||||||
|
[routerLink]="['./' + page]">
|
||||||
|
{{ type + '.edit.tabs.' + page + '.head' | translate}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-pane active">
|
||||||
|
<div class="mb-4">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
<a *ngIf="!hideReturnButton" [routerLink]="getPageUrl((dsoRD$ | async)?.payload)" class="btn btn-outline-secondary">{{ type + '.edit.return' | translate }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -1,5 +1,4 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
@@ -10,21 +9,13 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { EditComColPageComponent } from './edit-comcol-page.component';
|
import { EditComColPageComponent } from './edit-comcol-page.component';
|
||||||
import { DataService } from '../../../core/data/data.service';
|
|
||||||
import {
|
|
||||||
createFailedRemoteDataObject$,
|
|
||||||
createSuccessfulRemoteDataObject$
|
|
||||||
} from '../../testing/utils';
|
|
||||||
|
|
||||||
describe('EditComColPageComponent', () => {
|
describe('EditComColPageComponent', () => {
|
||||||
let comp: EditComColPageComponent<DSpaceObject>;
|
let comp: EditComColPageComponent<DSpaceObject>;
|
||||||
let fixture: ComponentFixture<EditComColPageComponent<DSpaceObject>>;
|
let fixture: ComponentFixture<EditComColPageComponent<DSpaceObject>>;
|
||||||
let dsoDataService: CommunityDataService;
|
|
||||||
let router: Router;
|
let router: Router;
|
||||||
|
|
||||||
let community;
|
let community;
|
||||||
let newCommunity;
|
|
||||||
let communityDataServiceStub;
|
|
||||||
let routerStub;
|
let routerStub;
|
||||||
let routeStub;
|
let routeStub;
|
||||||
|
|
||||||
@@ -37,25 +28,33 @@ describe('EditComColPageComponent', () => {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
newCommunity = Object.assign(new Community(), {
|
|
||||||
uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48',
|
|
||||||
metadata: [{
|
|
||||||
key: 'dc.title',
|
|
||||||
value: 'new community'
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
communityDataServiceStub = {
|
|
||||||
update: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity)
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
routerStub = {
|
routerStub = {
|
||||||
navigate: (commands) => commands
|
navigate: (commands) => commands,
|
||||||
|
events: observableOf({}),
|
||||||
|
url: 'mockUrl'
|
||||||
};
|
};
|
||||||
|
|
||||||
routeStub = {
|
routeStub = {
|
||||||
data: observableOf(community)
|
data: observableOf({
|
||||||
|
dso: community
|
||||||
|
}),
|
||||||
|
routeConfig: {
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'mockUrl',
|
||||||
|
data: {
|
||||||
|
hideReturnButton: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshot: {
|
||||||
|
firstChild: {
|
||||||
|
routeConfig: {
|
||||||
|
path: 'mockUrl'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -65,7 +64,6 @@ describe('EditComColPageComponent', () => {
|
|||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: DataService, useValue: communityDataServiceStub },
|
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
],
|
],
|
||||||
@@ -77,33 +75,16 @@ describe('EditComColPageComponent', () => {
|
|||||||
fixture = TestBed.createComponent(EditComColPageComponent);
|
fixture = TestBed.createComponent(EditComColPageComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
dsoDataService = (comp as any).dsoDataService;
|
|
||||||
router = (comp as any).router;
|
router = (comp as any).router;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onSubmit', () => {
|
describe('getPageUrl', () => {
|
||||||
let data;
|
let url;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
data = Object.assign(new Community(), {
|
url = comp.getPageUrl(community);
|
||||||
metadata: [{
|
|
||||||
key: 'dc.title',
|
|
||||||
value: 'test'
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
it('should navigate when successful', () => {
|
it('should return the current url as a fallback', () => {
|
||||||
spyOn(router, 'navigate');
|
expect(url).toEqual(routerStub.url);
|
||||||
comp.onSubmit(data);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(router.navigate).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not navigate on failure', () => {
|
|
||||||
spyOn(router, 'navigate');
|
|
||||||
spyOn(dsoDataService, 'update').and.returnValue(createFailedRemoteDataObject$(newCommunity));
|
|
||||||
comp.onSubmit(data);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(router.navigate).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { isNotUndefined } from '../../empty.util';
|
import { isNotEmpty, isNotUndefined } from '../../empty.util';
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { DataService } from '../../../core/data/data.service';
|
import { DataService } from '../../../core/data/data.service';
|
||||||
@@ -17,37 +17,54 @@ import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
|||||||
})
|
})
|
||||||
export class EditComColPageComponent<TDomain extends DSpaceObject> implements OnInit {
|
export class EditComColPageComponent<TDomain extends DSpaceObject> implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Frontend endpoint for this type of DSO
|
* The type of DSpaceObject (used to create i18n messages)
|
||||||
*/
|
*/
|
||||||
protected frontendURL: string;
|
public type: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The initial DSO object
|
* The current page outlet string
|
||||||
|
*/
|
||||||
|
public currentPage: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All possible page outlet strings
|
||||||
|
*/
|
||||||
|
public pages: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DSO to render the edit page for
|
||||||
*/
|
*/
|
||||||
public dsoRD$: Observable<RemoteData<TDomain>>;
|
public dsoRD$: Observable<RemoteData<TDomain>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the default return button?
|
||||||
|
*/
|
||||||
|
public hideReturnButton: boolean;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected dsoDataService: DataService<TDomain>,
|
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected route: ActivatedRoute
|
protected route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
|
this.router.events.subscribe(() => {
|
||||||
|
this.currentPage = this.route.snapshot.firstChild.routeConfig.path;
|
||||||
|
this.hideReturnButton = this.route.routeConfig.children
|
||||||
|
.find((child: any) => child.path === this.currentPage).data.hideReturnButton;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.pages = this.route.routeConfig.children
|
||||||
|
.map((child: any) => child.path)
|
||||||
|
.filter((path: string) => isNotEmpty(path)); // ignore reroutes
|
||||||
this.dsoRD$ = this.route.data.pipe(first(), map((data) => data.dso));
|
this.dsoRD$ = this.route.data.pipe(first(), map((data) => data.dso));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TDomain} dso The updated version of the DSO
|
* Get the dso's page url
|
||||||
* Updates an existing DSO based on the submitted user data and navigates to the edited object's home page
|
* This method is expected to be overridden in the edit community/collection page components
|
||||||
|
* @param dso The DSpaceObject for which the url is requested
|
||||||
*/
|
*/
|
||||||
onSubmit(dso: TDomain) {
|
getPageUrl(dso: TDomain): string {
|
||||||
this.dsoDataService.update(dso)
|
return this.router.url;
|
||||||
.pipe(getSucceededRemoteData())
|
|
||||||
.subscribe((dsoRD: RemoteData<TDomain>) => {
|
|
||||||
if (isNotUndefined(dsoRD)) {
|
|
||||||
const newUUID = dsoRD.payload.uuid;
|
|
||||||
this.router.navigate([this.frontendURL + newUUID]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ import {
|
|||||||
DynamicFormControl,
|
DynamicFormControl,
|
||||||
DynamicFormControlContainerComponent,
|
DynamicFormControlContainerComponent,
|
||||||
DynamicFormControlEvent,
|
DynamicFormControlEvent,
|
||||||
DynamicFormControlModel,
|
DynamicFormControlModel, DynamicFormInstancesService,
|
||||||
DynamicFormLayout,
|
DynamicFormLayout,
|
||||||
DynamicFormLayoutService,
|
DynamicFormLayoutService,
|
||||||
DynamicFormValidationService,
|
DynamicFormValidationService,
|
||||||
@@ -73,8 +73,8 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a
|
|||||||
import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components';
|
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 { 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 { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component';
|
||||||
import { map, switchMap, take } from 'rxjs/operators';
|
import { map, startWith, switchMap, find, take } from 'rxjs/operators';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||||
import { SearchResult } from '../../../search/search-result.model';
|
import { SearchResult } from '../../../search/search-result.model';
|
||||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||||
@@ -96,7 +96,6 @@ import { ItemSearchResult } from '../../../object-collection/shared/item-search-
|
|||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
||||||
import { FormService } from '../../form.service';
|
import { FormService } from '../../form.service';
|
||||||
import { combineLatest as observableCombineLatest } from 'rxjs';
|
|
||||||
|
|
||||||
export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null {
|
export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null {
|
||||||
switch (model.type) {
|
switch (model.type) {
|
||||||
@@ -208,6 +207,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected componentFactoryResolver: ComponentFactoryResolver,
|
protected componentFactoryResolver: ComponentFactoryResolver,
|
||||||
|
protected dynamicFormInstanceService: DynamicFormInstancesService,
|
||||||
protected layoutService: DynamicFormLayoutService,
|
protected layoutService: DynamicFormLayoutService,
|
||||||
protected validationService: DynamicFormValidationService,
|
protected validationService: DynamicFormValidationService,
|
||||||
protected translateService: TranslateService,
|
protected translateService: TranslateService,
|
||||||
@@ -222,7 +222,8 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
|
|||||||
private ref: ChangeDetectorRef,
|
private ref: ChangeDetectorRef,
|
||||||
private formService: FormService
|
private formService: FormService
|
||||||
) {
|
) {
|
||||||
super(componentFactoryResolver, layoutService, validationService);
|
|
||||||
|
super(componentFactoryResolver, layoutService, validationService, dynamicFormInstanceService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -26,6 +26,9 @@ import { DynamicConcatModel } from '../models/ds-dynamic-concat.model';
|
|||||||
import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions';
|
import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions';
|
||||||
|
|
||||||
// tslint:disable:max-classes-per-file
|
// tslint:disable:max-classes-per-file
|
||||||
|
/**
|
||||||
|
* Abstract class that defines objects that can be reordered
|
||||||
|
*/
|
||||||
export abstract class Reorderable {
|
export abstract class Reorderable {
|
||||||
|
|
||||||
constructor(public oldIndex?: number, public newIndex?: number) {
|
constructor(public oldIndex?: number, public newIndex?: number) {
|
||||||
@@ -81,6 +84,9 @@ export class ReorderableFormFieldMetadataValue extends Reorderable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single relationship that can be reordered in a list of multiple relationships
|
||||||
|
*/
|
||||||
export class ReorderableRelationship extends Reorderable {
|
export class ReorderableRelationship extends Reorderable {
|
||||||
|
|
||||||
constructor(public relationship: Relationship, public useLeftItem: boolean, protected relationshipService: RelationshipService, oldIndex?: number, newIndex?: number) {
|
constructor(public relationship: Relationship, public useLeftItem: boolean, protected relationshipService: RelationshipService, oldIndex?: number, newIndex?: number) {
|
||||||
@@ -114,6 +120,9 @@ export class ReorderableRelationship extends Reorderable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single existing relationship value as metadata in submission
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-existing-metadata-list-element',
|
selector: 'ds-existing-metadata-list-element',
|
||||||
templateUrl: './existing-metadata-list-element.component.html',
|
templateUrl: './existing-metadata-list-element.component.html',
|
||||||
@@ -160,6 +169,9 @@ export class ExistingMetadataListElementComponent implements OnChanges, OnDestro
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the selected relationship from the list
|
||||||
|
*/
|
||||||
removeSelection() {
|
removeSelection() {
|
||||||
this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: this.relatedItem }));
|
this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: this.relatedItem }));
|
||||||
this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, this.relatedItem, this.relationshipOptions.relationshipType, this.submissionId))
|
this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, this.relatedItem, this.relationshipOptions.relationshipType, this.submissionId))
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
import { DynamicDateControlModel, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core';
|
import {
|
||||||
import { DynamicDateControlModelConfig } from '@ng-dynamic-forms/core/src/model/dynamic-date-control.model';
|
DynamicDateControlModel,
|
||||||
|
DynamicDateControlModelConfig,
|
||||||
|
DynamicFormControlLayout,
|
||||||
|
serializable
|
||||||
|
} from '@ng-dynamic-forms/core';
|
||||||
|
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export const DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER = 'DATE';
|
export const DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER = 'DATE';
|
||||||
|
@@ -1,7 +1,12 @@
|
|||||||
import { DynamicFormControlLayout, DynamicFormGroupModel, serializable } from '@ng-dynamic-forms/core';
|
import {
|
||||||
|
DynamicFormControlLayout,
|
||||||
|
DynamicFormGroupModel,
|
||||||
|
DynamicFormGroupModelConfig,
|
||||||
|
serializable
|
||||||
|
} from '@ng-dynamic-forms/core';
|
||||||
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core/src/model/form-group/dynamic-form-group.model';
|
|
||||||
import { LanguageCode } from '../../models/form-field-language-value.model';
|
import { LanguageCode } from '../../models/form-field-language-value.model';
|
||||||
|
|
||||||
export const QUALDROP_GROUP_SUFFIX = '_QUALDROP_GROUP';
|
export const QUALDROP_GROUP_SUFFIX = '_QUALDROP_GROUP';
|
||||||
|
@@ -113,7 +113,7 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should emit the page filtered from not yet selected objects and call select on the service for all objects', () => {
|
it('should emit the page filtered from not yet selected objects and call select on the service for all objects', () => {
|
||||||
expect(component.deselectObject.emit).toHaveBeenCalledWith(searchResult1, searchResult2);
|
expect((component.deselectObject as any).emit).toHaveBeenCalledWith(searchResult1, searchResult2);
|
||||||
expect(selectableListService.deselect).toHaveBeenCalledWith(listID, [searchResult1, searchResult2, searchResult3]);
|
expect(selectableListService.deselect).toHaveBeenCalledWith(listID, [searchResult1, searchResult2, searchResult3]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -137,7 +137,7 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should emit the page filtered from not yet selected objects and call select on the service for all objects', () => {
|
it('should emit the page filtered from not yet selected objects and call select on the service for all objects', () => {
|
||||||
expect(component.deselectObject.emit).toHaveBeenCalledWith(searchResult1, searchResult2);
|
expect((component.deselectObject as any).emit).toHaveBeenCalledWith(searchResult1, searchResult2);
|
||||||
expect(selectableListService.deselectAll).toHaveBeenCalledWith(listID);
|
expect(selectableListService.deselectAll).toHaveBeenCalledWith(listID);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -15,7 +15,6 @@ import { Router } from '@angular/router';
|
|||||||
import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
|
import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
|
||||||
import { hasValue, isNotEmpty } from '../../../../../empty.util';
|
import { hasValue, isNotEmpty } from '../../../../../empty.util';
|
||||||
import { concat, map, multicast, switchMap, take, takeWhile, tap } from 'rxjs/operators';
|
import { concat, map, multicast, switchMap, take, takeWhile, tap } from 'rxjs/operators';
|
||||||
import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
|
|
||||||
import { getSucceededRemoteData } from '../../../../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../../../../core/shared/operators';
|
||||||
import { RouteService } from '../../../../../../core/services/route.service';
|
import { RouteService } from '../../../../../../core/services/route.service';
|
||||||
import { CollectionElementLinkType } from '../../../../../object-collection/collection-element-link.type';
|
import { CollectionElementLinkType } from '../../../../../object-collection/collection-element-link.type';
|
||||||
@@ -86,13 +85,13 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
|
|||||||
multicast(
|
multicast(
|
||||||
() => new ReplaySubject(1),
|
() => new ReplaySubject(1),
|
||||||
(subject) => subject.pipe(
|
(subject) => subject.pipe(
|
||||||
takeWhile((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => rd.isLoading),
|
takeWhile((rd: RemoteData<PaginatedList<SearchResult<Item>>>) => rd.isLoading),
|
||||||
concat(subject.pipe(take(1)))
|
concat(subject.pipe(take(1)))
|
||||||
)
|
)
|
||||||
) as any
|
) as any
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
);
|
) as Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -10,10 +10,8 @@ import {
|
|||||||
DynamicFormArrayModel,
|
DynamicFormArrayModel,
|
||||||
DynamicFormControlModel,
|
DynamicFormControlModel,
|
||||||
DynamicFormGroupModel,
|
DynamicFormGroupModel,
|
||||||
DynamicFormService,
|
DynamicFormService, DynamicFormValidationService,
|
||||||
DynamicFormValidationService,
|
DynamicPathable, parseReviver,
|
||||||
DynamicPathable,
|
|
||||||
JSONUtils,
|
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { isObject, isString, mergeWith } from 'lodash';
|
import { isObject, isString, mergeWith } from 'lodash';
|
||||||
|
|
||||||
@@ -205,7 +203,7 @@ export class FormBuilderService extends DynamicFormService {
|
|||||||
|
|
||||||
modelFromConfiguration(submissionId: string, json: string | SubmissionFormsModel, scopeUUID: string, sectionData: any = {}, submissionScope?: string, readOnly = false): DynamicFormControlModel[] | never {
|
modelFromConfiguration(submissionId: string, json: string | SubmissionFormsModel, scopeUUID: string, sectionData: any = {}, submissionScope?: string, readOnly = false): DynamicFormControlModel[] | never {
|
||||||
let rows: DynamicFormControlModel[] = [];
|
let rows: DynamicFormControlModel[] = [];
|
||||||
const rawData = typeof json === 'string' ? JSON.parse(json, JSONUtils.parseReviver) : json;
|
const rawData = typeof json === 'string' ? JSON.parse(json, parseReviver) : json;
|
||||||
|
|
||||||
if (rawData.rows && !isEmpty(rawData.rows)) {
|
if (rawData.rows && !isEmpty(rawData.rows)) {
|
||||||
rawData.rows.forEach((currentRow) => {
|
rawData.rows.forEach((currentRow) => {
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
import { AppState } from '../../app.reducer';
|
|
||||||
|
|
||||||
export const historySelector = (state: AppState) => state.history;
|
|
@@ -1,40 +1,43 @@
|
|||||||
import * as ngrx from '@ngrx/store';
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
import { MenuService } from './menu.service';
|
|
||||||
import { cold, hot } from 'jasmine-marbles';
|
|
||||||
import { MenuID } from './initial-menus-state';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import * as ngrx from '@ngrx/store';
|
||||||
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
|
import { provideMockStore } from '@ngrx/store/testing';
|
||||||
|
import { cold, hot } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
import { MenuService } from './menu.service';
|
||||||
|
import { MenuID } from './initial-menus-state';
|
||||||
import {
|
import {
|
||||||
ActivateMenuSectionAction,
|
ActivateMenuSectionAction,
|
||||||
AddMenuSectionAction,
|
AddMenuSectionAction,
|
||||||
CollapseMenuAction, CollapseMenuPreviewAction, DeactivateMenuSectionAction,
|
CollapseMenuAction,
|
||||||
ExpandMenuAction, ExpandMenuPreviewAction, HideMenuAction,
|
CollapseMenuPreviewAction,
|
||||||
RemoveMenuSectionAction, ShowMenuAction, ToggleActiveMenuSectionAction, ToggleMenuAction
|
DeactivateMenuSectionAction,
|
||||||
|
ExpandMenuAction,
|
||||||
|
ExpandMenuPreviewAction,
|
||||||
|
HideMenuAction,
|
||||||
|
RemoveMenuSectionAction,
|
||||||
|
ShowMenuAction,
|
||||||
|
ToggleActiveMenuSectionAction,
|
||||||
|
ToggleMenuAction
|
||||||
} from './menu.actions';
|
} from './menu.actions';
|
||||||
|
import { MenuSection, menusReducer } from './menu.reducer';
|
||||||
|
|
||||||
describe('MenuService', () => {
|
describe('MenuService', () => {
|
||||||
let service: MenuService;
|
let service: MenuService;
|
||||||
let selectSpy;
|
let selectSpy;
|
||||||
let store;
|
let store: any;
|
||||||
let fakeMenu;
|
let fakeMenu;
|
||||||
let visibleSection1;
|
let visibleSection1;
|
||||||
let visibleSection2;
|
let visibleSection2;
|
||||||
let hiddenSection3;
|
let hiddenSection3;
|
||||||
let subSection4;
|
let subSection4;
|
||||||
let topSections;
|
let topSections;
|
||||||
|
let initialState;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
store = Object.assign(observableOf({}), {
|
|
||||||
dispatch: () => {/***/
|
|
||||||
}
|
|
||||||
}) as any;
|
|
||||||
fakeMenu = {
|
|
||||||
id: MenuID.ADMIN,
|
|
||||||
collapsed: true,
|
|
||||||
visible: false,
|
|
||||||
previewCollapsed: true
|
|
||||||
} as any;
|
|
||||||
visibleSection1 = {
|
visibleSection1 = {
|
||||||
id: 'section',
|
id: 'section',
|
||||||
visible: true,
|
visible: true,
|
||||||
@@ -60,35 +63,44 @@ describe('MenuService', () => {
|
|||||||
section_3: hiddenSection3,
|
section_3: hiddenSection3,
|
||||||
section_4: subSection4
|
section_4: subSection4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fakeMenu = {
|
||||||
|
id: MenuID.ADMIN,
|
||||||
|
collapsed: true,
|
||||||
|
visible: false,
|
||||||
|
sections: topSections,
|
||||||
|
previewCollapsed: true
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
initialState = {
|
||||||
|
menus: {
|
||||||
|
'admin-sidebar' : fakeMenu
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
init();
|
init();
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
StoreModule.forRoot({ menus: menusReducer })
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: Store, useValue: store },
|
provideMockStore({ initialState }),
|
||||||
{ provide: MenuService, useValue: service }
|
{ provide: MenuService, useValue: service }
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
store = TestBed.get(Store);
|
||||||
service = new MenuService(store);
|
service = new MenuService(store);
|
||||||
selectSpy = spyOnProperty(ngrx, 'select');
|
selectSpy = spyOnProperty(ngrx, 'select').and.callThrough();
|
||||||
spyOn(store, 'dispatch');
|
spyOn(store, 'dispatch');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getMenu', () => {
|
describe('getMenu', () => {
|
||||||
beforeEach(() => {
|
|
||||||
selectSpy.and.callFake(() => {
|
|
||||||
return () => {
|
|
||||||
return () => hot('a', {
|
|
||||||
a: fakeMenu
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should return the menu', () => {
|
it('should return the menu', () => {
|
||||||
|
|
||||||
const result = service.getMenu(MenuID.ADMIN);
|
const result = service.getMenu(MenuID.ADMIN);
|
||||||
@@ -136,7 +148,7 @@ describe('MenuService', () => {
|
|||||||
describe('when the subsection list is not empty', () => {
|
describe('when the subsection list is not empty', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getMenuSection').and.returnValue(observableOf(visibleSection1));
|
spyOn(service, 'getMenuSection').and.returnValue(observableOf(visibleSection1 as MenuSection));
|
||||||
selectSpy.and.callFake(() => {
|
selectSpy.and.callFake(() => {
|
||||||
return () => {
|
return () => {
|
||||||
return () => hot('a', {
|
return () => hot('a', {
|
||||||
@@ -225,25 +237,25 @@ describe('MenuService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getMenuSection', () => {
|
describe('getMenuSection', () => {
|
||||||
beforeEach(() => {
|
it('should return menu section', () => {
|
||||||
selectSpy.and.callFake(() => {
|
|
||||||
return () => {
|
|
||||||
return () => hot('a', {
|
|
||||||
a: hiddenSection3
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should return false', () => {
|
|
||||||
|
|
||||||
const result = service.getMenuSection(MenuID.ADMIN, 'fakeId');
|
const result = service.getMenuSection(MenuID.ADMIN, 'section_3');
|
||||||
const expected = cold('b', {
|
const expected = cold('b', {
|
||||||
b: hiddenSection3
|
b: hiddenSection3
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeObservable(expected);
|
expect(result).toBeObservable(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return undefined', () => {
|
||||||
|
|
||||||
|
const result = service.getMenuSection(MenuID.ADMIN, 'fake');
|
||||||
|
const expected = cold('b', {
|
||||||
|
b: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isMenuCollapsed', () => {
|
describe('isMenuCollapsed', () => {
|
||||||
@@ -294,7 +306,7 @@ describe('MenuService', () => {
|
|||||||
|
|
||||||
describe('isSectionActive', () => {
|
describe('isSectionActive', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getMenuSection').and.returnValue(observableOf(visibleSection1));
|
spyOn(service, 'getMenuSection').and.returnValue(observableOf(visibleSection1 as MenuSection));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when the section is not active', () => {
|
it('should return false when the section is not active', () => {
|
||||||
@@ -309,7 +321,7 @@ describe('MenuService', () => {
|
|||||||
|
|
||||||
describe('isSectionVisible', () => {
|
describe('isSectionVisible', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(service, 'getMenuSection').and.returnValue(observableOf(hiddenSection3));
|
spyOn(service, 'getMenuSection').and.returnValue(observableOf(hiddenSection3 as MenuSection));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when the section is hidden', () => {
|
it('should return false when the section is hidden', () => {
|
||||||
|
@@ -1,24 +1,35 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { MenuSection, MenuSectionIndex, MenuSections, MenusState, MenuState } from './menu.reducer';
|
import { MenuSection, MenuSections, MenuState } from './menu.reducer';
|
||||||
import { AppState, keySelector } from '../../app.reducer';
|
import { AppState, keySelector } from '../../app.reducer';
|
||||||
import { MenuID } from './initial-menus-state';
|
import { MenuID } from './initial-menus-state';
|
||||||
import { Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
ActivateMenuSectionAction,
|
ActivateMenuSectionAction,
|
||||||
AddMenuSectionAction,
|
AddMenuSectionAction,
|
||||||
CollapseMenuAction, CollapseMenuPreviewAction,
|
CollapseMenuAction,
|
||||||
|
CollapseMenuPreviewAction,
|
||||||
DeactivateMenuSectionAction,
|
DeactivateMenuSectionAction,
|
||||||
ExpandMenuAction, ExpandMenuPreviewAction,
|
ExpandMenuAction,
|
||||||
|
ExpandMenuPreviewAction,
|
||||||
HideMenuAction,
|
HideMenuAction,
|
||||||
RemoveMenuSectionAction,
|
RemoveMenuSectionAction,
|
||||||
ShowMenuAction,
|
ShowMenuAction,
|
||||||
ToggleActiveMenuSectionAction,
|
ToggleActiveMenuSectionAction,
|
||||||
ToggleMenuAction,
|
ToggleMenuAction,
|
||||||
} from './menu.actions';
|
} from './menu.actions';
|
||||||
import { hasNoValue, isNotEmpty } from '../empty.util';
|
import { hasNoValue, hasValue, isNotEmpty } from '../empty.util';
|
||||||
import { combineLatest as observableCombineLatest } from 'rxjs';
|
|
||||||
|
export function menuKeySelector<T>(key: string, selector): MemoizedSelector<MenuState, T> {
|
||||||
|
return createSelector(selector, (state) => {
|
||||||
|
if (hasValue(state)) {
|
||||||
|
return state[key];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const menusStateSelector = (state) => state.menus;
|
const menusStateSelector = (state) => state.menus;
|
||||||
|
|
||||||
@@ -28,20 +39,20 @@ const menuByIDSelector = (menuID: MenuID): MemoizedSelector<AppState, MenuState>
|
|||||||
|
|
||||||
const menuSectionStateSelector = (state: MenuState) => state.sections;
|
const menuSectionStateSelector = (state: MenuState) => state.sections;
|
||||||
|
|
||||||
const menuSectionByIDSelector = (id: string): MemoizedSelector<AppState, MenuSection> => {
|
const menuSectionByIDSelector = (id: string): MemoizedSelector<MenuState, MenuSection> => {
|
||||||
return keySelector<MenuSection>(id, menuSectionStateSelector);
|
return menuKeySelector<MenuSection>(id, menuSectionStateSelector);
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuSectionIndexStateSelector = (state: MenuState) => state.sectionToSubsectionIndex;
|
const menuSectionIndexStateSelector = (state: MenuState) => state.sectionToSubsectionIndex;
|
||||||
|
|
||||||
const getSubSectionsFromSectionSelector = (id: string): MemoizedSelector<AppState, MenuSectionIndex> => {
|
const getSubSectionsFromSectionSelector = (id: string): MemoizedSelector<MenuState, string[]> => {
|
||||||
return keySelector<MenuSectionIndex>(id, menuSectionIndexStateSelector);
|
return menuKeySelector<string[]>(id, menuSectionIndexStateSelector);
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MenuService {
|
export class MenuService {
|
||||||
|
|
||||||
constructor(private store: Store<MenusState>) {
|
constructor(private store: Store<AppState>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,4 +3,7 @@ export class AuthServiceMock {
|
|||||||
public checksAuthenticationToken() {
|
public checksAuthenticationToken() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
public buildAuthHeader() {
|
||||||
|
return 'auth-header';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -88,7 +88,7 @@ describe('ItemDetailPreviewComponent', () => {
|
|||||||
component.object = { hitHighlights: {} } as any;
|
component.object = { hitHighlights: {} } as any;
|
||||||
component.item = mockItem;
|
component.item = mockItem;
|
||||||
component.separator = ', ';
|
component.separator = ', ';
|
||||||
spyOn(component.item, 'getFiles').and.returnValue(mockItem.bundles);
|
spyOn(component.item, 'getFiles').and.returnValue(mockItem.bundles as any);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@@ -13,7 +13,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
|||||||
describe('ObjectDetailComponent', () => {
|
describe('ObjectDetailComponent', () => {
|
||||||
let comp: ObjectDetailComponent;
|
let comp: ObjectDetailComponent;
|
||||||
let fixture: ComponentFixture<ObjectDetailComponent>;
|
let fixture: ComponentFixture<ObjectDetailComponent>;
|
||||||
const testEvent = {test: 'test'};
|
const testEvent: any = {test: 'test'};
|
||||||
|
|
||||||
const testObjects = [
|
const testObjects = [
|
||||||
Object.assign (new DSpaceObject(), { one: 1 }),
|
Object.assign (new DSpaceObject(), { one: 1 }),
|
||||||
@@ -27,7 +27,7 @@ describe('ObjectDetailComponent', () => {
|
|||||||
Object.assign (new DSpaceObject(), { nine: 9 }),
|
Object.assign (new DSpaceObject(), { nine: 9 }),
|
||||||
Object.assign (new DSpaceObject(), { ten: 10 }),
|
Object.assign (new DSpaceObject(), { ten: 10 }),
|
||||||
];
|
];
|
||||||
const pageInfo = Object.assign(new PageInfo(), {elementsPerPage: 1, totalElements: 10, totalPages: 10, currentPage: 1})
|
const pageInfo = Object.assign(new PageInfo(), {elementsPerPage: 1, totalElements: 10, totalPages: 10, currentPage: 1});
|
||||||
const mockRD = createSuccessfulRemoteDataObject(new PaginatedList(pageInfo, testObjects));
|
const mockRD = createSuccessfulRemoteDataObject(new PaginatedList(pageInfo, testObjects));
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
|
@@ -7,7 +7,7 @@ import { SelectableListService } from './selectable-list/selectable-list.service
|
|||||||
describe('ObjectListComponent', () => {
|
describe('ObjectListComponent', () => {
|
||||||
let comp: ObjectListComponent;
|
let comp: ObjectListComponent;
|
||||||
let fixture: ComponentFixture<ObjectListComponent>;
|
let fixture: ComponentFixture<ObjectListComponent>;
|
||||||
const testEvent = { test: 'test' };
|
const testEvent: any = { test: 'test' };
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
@@ -1,10 +1,15 @@
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { async, TestBed } from '@angular/core/testing';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
import { SelectableListService } from './selectable-list.service';
|
import { SelectableListService } from './selectable-list.service';
|
||||||
import { SelectableListsState } from './selectable-list.reducer';
|
|
||||||
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
||||||
import { hasValue } from '../../empty.util';
|
import { hasValue } from '../../empty.util';
|
||||||
import { SelectableListDeselectAction, SelectableListDeselectSingleAction, SelectableListSelectAction, SelectableListSelectSingleAction } from './selectable-list.actions';
|
import {
|
||||||
|
SelectableListDeselectAction,
|
||||||
|
SelectableListDeselectSingleAction,
|
||||||
|
SelectableListSelectAction,
|
||||||
|
SelectableListSelectSingleAction
|
||||||
|
} from './selectable-list.actions';
|
||||||
|
import { AppState } from '../../../app.reducer';
|
||||||
|
|
||||||
class SelectableObject extends ListableObject {
|
class SelectableObject extends ListableObject {
|
||||||
constructor(private value: string) {
|
constructor(private value: string) {
|
||||||
@@ -33,7 +38,7 @@ describe('SelectableListService', () => {
|
|||||||
const selected4 = new SelectableObject(value4);
|
const selected4 = new SelectableObject(value4);
|
||||||
|
|
||||||
let service: SelectableListService;
|
let service: SelectableListService;
|
||||||
const store: Store<SelectableListsState> = jasmine.createSpyObj('store', {
|
const store: Store<AppState> = jasmine.createSpyObj('store', {
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
dispatch: {},
|
dispatch: {},
|
||||||
/* tslint:enable:no-empty */
|
/* tslint:enable:no-empty */
|
||||||
|
@@ -1,18 +1,20 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, startWith, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||||
import { SelectableListsState, SelectableListState } from './selectable-list.reducer';
|
import { SelectableListState } from './selectable-list.reducer';
|
||||||
import { AppState, keySelector } from '../../../app.reducer';
|
import { AppState, keySelector } from '../../../app.reducer';
|
||||||
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
||||||
import {
|
import {
|
||||||
SelectableListDeselectAction, SelectableListDeselectAllAction,
|
SelectableListDeselectAction,
|
||||||
SelectableListDeselectSingleAction, SelectableListSelectAction,
|
SelectableListDeselectAllAction,
|
||||||
|
SelectableListDeselectSingleAction,
|
||||||
|
SelectableListSelectAction,
|
||||||
SelectableListSelectSingleAction
|
SelectableListSelectSingleAction
|
||||||
} from './selectable-list.actions';
|
} from './selectable-list.actions';
|
||||||
import { hasNoValue, hasValue, isNotEmpty } from '../../empty.util';
|
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||||
|
|
||||||
const selectableListsStateSelector = (state) => state.selectableLists;
|
const selectableListsStateSelector = (state: AppState) => state.selectableLists;
|
||||||
|
|
||||||
const menuByIDSelector = (id: string): MemoizedSelector<AppState, SelectableListState> => {
|
const menuByIDSelector = (id: string): MemoizedSelector<AppState, SelectableListState> => {
|
||||||
return keySelector<SelectableListState>(id, selectableListsStateSelector);
|
return keySelector<SelectableListState>(id, selectableListsStateSelector);
|
||||||
@@ -21,7 +23,7 @@ const menuByIDSelector = (id: string): MemoizedSelector<AppState, SelectableList
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class SelectableListService {
|
export class SelectableListService {
|
||||||
|
|
||||||
constructor(private store: Store<SelectableListsState>) {
|
constructor(private store: Store<AppState>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -151,6 +151,8 @@ import { DsDynamicDisabledComponent } from './form/builder/ds-dynamic-form-ui/mo
|
|||||||
import { DsDynamicLookupRelationSearchTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component';
|
import { DsDynamicLookupRelationSearchTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component';
|
||||||
import { DsDynamicLookupRelationSelectionTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component';
|
import { DsDynamicLookupRelationSelectionTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component';
|
||||||
import { PageSizeSelectorComponent } from './page-size-selector/page-size-selector.component';
|
import { PageSizeSelectorComponent } from './page-size-selector/page-size-selector.component';
|
||||||
|
import { AbstractTrackableComponent } from './trackable/abstract-trackable.component';
|
||||||
|
import { ComcolMetadataComponent } from './comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
|
||||||
import { ItemSelectComponent } from './object-select/item-select/item-select.component';
|
import { ItemSelectComponent } from './object-select/item-select/item-select.component';
|
||||||
import { CollectionSelectComponent } from './object-select/collection-select/collection-select.component';
|
import { CollectionSelectComponent } from './object-select/collection-select/collection-select.component';
|
||||||
import { FilterInputSuggestionsComponent } from './input-suggestions/filter-suggestions/filter-input-suggestions.component';
|
import { FilterInputSuggestionsComponent } from './input-suggestions/filter-suggestions/filter-input-suggestions.component';
|
||||||
@@ -326,6 +328,8 @@ const COMPONENTS = [
|
|||||||
CollectionGridElementComponent,
|
CollectionGridElementComponent,
|
||||||
CommunityGridElementComponent,
|
CommunityGridElementComponent,
|
||||||
BrowseByComponent,
|
BrowseByComponent,
|
||||||
|
AbstractTrackableComponent,
|
||||||
|
ComcolMetadataComponent,
|
||||||
ItemTypeBadgeComponent,
|
ItemTypeBadgeComponent,
|
||||||
ItemSelectComponent,
|
ItemSelectComponent,
|
||||||
CollectionSelectComponent,
|
CollectionSelectComponent,
|
||||||
@@ -403,6 +407,7 @@ const SHARED_ITEM_PAGE_COMPONENTS = [
|
|||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
TruncatableService,
|
TruncatableService,
|
||||||
MockAdminGuard,
|
MockAdminGuard,
|
||||||
|
AbstractTrackableComponent,
|
||||||
{
|
{
|
||||||
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
|
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
|
||||||
useValue: dsDynamicFormControlMapFn
|
useValue: dsDynamicFormControlMapFn
|
||||||
|
109
src/app/shared/sidebar/filter/sidebar-filter.service.spec.ts
Normal file
109
src/app/shared/sidebar/filter/sidebar-filter.service.spec.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
import * as ngrx from '@ngrx/store';
|
||||||
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
|
import { provideMockStore } from '@ngrx/store/testing';
|
||||||
|
import { cold } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
import { sidebarFilterReducer } from './sidebar-filter.reducer';
|
||||||
|
import { SidebarFilterService } from './sidebar-filter.service';
|
||||||
|
import {
|
||||||
|
FilterCollapseAction,
|
||||||
|
FilterExpandAction,
|
||||||
|
FilterInitializeAction,
|
||||||
|
FilterToggleAction
|
||||||
|
} from './sidebar-filter.actions';
|
||||||
|
|
||||||
|
describe('SidebarFilterService', () => {
|
||||||
|
let service: SidebarFilterService;
|
||||||
|
let selectSpy;
|
||||||
|
let store: any;
|
||||||
|
let initialState;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
initialState = {
|
||||||
|
sidebarFilter: {
|
||||||
|
filter_1 : {
|
||||||
|
filterCollapsed: true
|
||||||
|
},
|
||||||
|
filter_2 : {
|
||||||
|
filterCollapsed: false
|
||||||
|
},
|
||||||
|
filter_3 : {
|
||||||
|
filterCollapsed: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
init();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
StoreModule.forRoot({ sidebarFilter: sidebarFilterReducer })
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
provideMockStore({ initialState }),
|
||||||
|
{ provide: SidebarFilterService, useValue: service }
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
store = TestBed.get(Store);
|
||||||
|
service = new SidebarFilterService(store);
|
||||||
|
selectSpy = spyOnProperty(ngrx, 'select').and.callThrough();
|
||||||
|
spyOn(store, 'dispatch');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initializeFilter', () => {
|
||||||
|
it('should dispatch an FilterInitializeAction with the correct arguments', () => {
|
||||||
|
service.initializeFilter('fakeFilter', true);
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new FilterInitializeAction('fakeFilter', true));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('collapse', () => {
|
||||||
|
it('should dispatch an FilterInitializeAction with the correct arguments', () => {
|
||||||
|
service.collapse('fakeFilter');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new FilterCollapseAction('fakeFilter'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('expand', () => {
|
||||||
|
it('should dispatch an FilterInitializeAction with the correct arguments', () => {
|
||||||
|
service.expand('fakeFilter');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new FilterExpandAction('fakeFilter'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggle', () => {
|
||||||
|
it('should dispatch an FilterInitializeAction with the correct arguments', () => {
|
||||||
|
service.toggle('fakeFilter');
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new FilterToggleAction('fakeFilter'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isCollapsed', () => {
|
||||||
|
it('should return true', () => {
|
||||||
|
|
||||||
|
const result = service.isCollapsed('filter_1');
|
||||||
|
const expected = cold('b', {
|
||||||
|
b: true
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false', () => {
|
||||||
|
|
||||||
|
const result = service.isCollapsed('filter_2');
|
||||||
|
const expected = cold('b', {
|
||||||
|
b: false
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user