Merge branch 'main' into Removing-unnecessary-circular-dependencies

This commit is contained in:
lotte
2022-04-20 14:47:55 +02:00
280 changed files with 7366 additions and 8121 deletions

View File

@@ -2,10 +2,16 @@
# For additional information regarding the format and rule options, please see: # For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries # https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running: # You can see what browsers were selected by your queries by running:
# npx browserslist # npx browserslist
> 0.5% last 1 Chrome version
last 2 versions last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR Firefox ESR
not IE 9-11 # For IE 9-11 support, remove 'not'. not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

222
.eslintrc.json Normal file
View File

@@ -0,0 +1,222 @@
{
"root": true,
"plugins": [
"@typescript-eslint",
"@angular-eslint/eslint-plugin",
"eslint-plugin-import",
"eslint-plugin-jsdoc",
"eslint-plugin-deprecation",
"eslint-plugin-unused-imports"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"./tsconfig.json",
"./cypress/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"max-classes-per-file": [
"error",
1
],
"comma-dangle": [
"off",
"always-multiline"
],
"eol-last": [
"error",
"always"
],
"no-console": [
"error",
{
"allow": [
"log",
"warn",
"dir",
"timeLog",
"assert",
"clear",
"count",
"countReset",
"group",
"groupEnd",
"table",
"debug",
"info",
"dirxml",
"error",
"groupCollapsed",
"Console",
"profile",
"profileEnd",
"timeStamp",
"context"
]
}
],
"curly": "error",
"brace-style": [
"error",
"1tbs",
{
"allowSingleLine": true
}
],
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"radix": "error",
"guard-for-in": "error",
"no-bitwise": "error",
"no-restricted-imports": "error",
"no-caller": "error",
"no-debugger": "error",
"no-redeclare": "error",
"no-eval": "error",
"no-fallthrough": "error",
"no-trailing-spaces": "error",
"space-infix-ops": "error",
"keyword-spacing": "error",
"no-var": "error",
"no-unused-expressions": [
"error",
{
"allowTernary": true
}
],
"prefer-const": "off", // todo: re-enable & fix errors (more strict than it used to be in TSLint)
"prefer-spread": "off",
"no-underscore-dangle": "off",
// todo: disabled rules from eslint:recommended, consider re-enabling & fixing
"no-prototype-builtins": "off",
"no-useless-escape": "off",
"no-case-declarations": "off",
"no-extra-boolean-cast": "off",
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "ds",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "ds",
"style": "kebab-case"
}
],
"@angular-eslint/pipe-prefix": [
"error",
{
"prefixes": [
"ds"
]
}
],
"@angular-eslint/no-attribute-decorator": "error",
"@angular-eslint/no-forward-ref": "error",
"@angular-eslint/no-output-native": "warn",
"@angular-eslint/no-output-on-prefix": "warn",
"@angular-eslint/no-conflicting-lifecycle": "warn",
"@typescript-eslint/no-inferrable-types":[
"error",
{
"ignoreParameters": true
}
],
"@typescript-eslint/quotes": [
"error",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"@typescript-eslint/semi": "error",
"@typescript-eslint/no-shadow": "error",
"@typescript-eslint/dot-notation": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "property",
"format": null
}
],
"@typescript-eslint/member-ordering": [
"error",
{
"default": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unified-signatures": "error",
"@typescript-eslint/ban-types": "warn", // todo: deal with {} type issues & re-enable
"@typescript-eslint/no-floating-promises": "warn",
"@typescript-eslint/no-misused-promises": "warn",
"@typescript-eslint/restrict-plus-operands": "warn",
"@typescript-eslint/unbound-method": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/require-await": "off",
"deprecation/deprecation": "warn",
"import/order": "off",
"import/no-deprecated": "warn"
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {
// todo: re-enable & fix errors
"@angular-eslint/template/no-negated-async": "off",
"@angular-eslint/template/eqeqeq": "off"
}
}
]
}

View File

@@ -70,7 +70,7 @@ jobs:
run: yarn install --frozen-lockfile run: yarn install --frozen-lockfile
- name: Run lint - name: Run lint
run: yarn run lint run: yarn run lint --quiet
- name: Check for circular dependencies - name: Check for circular dependencies
run: yarn run check-circ-deps run: yarn run check-circ-deps
@@ -131,6 +131,14 @@ jobs:
name: e2e-test-screenshots name: e2e-test-screenshots
path: cypress/screenshots path: cypress/screenshots
- name: Stop app (in case it stays up after e2e tests)
run: |
app_pid=$(lsof -t -i:4000)
if [[ ! -z $app_pid ]]; then
echo "App was still up! (PID: $app_pid)"
kill -9 $app_pid
fi
# Start up the app with SSR enabled (run in background) # Start up the app with SSR enabled (run in background)
- name: Start app in SSR (server-side rendering) mode - name: Start app in SSR (server-side rendering) mode
run: | run: |

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/.angular/cache
/__build__ /__build__
/__server_build__ /__server_build__
/node_modules /node_modules

View File

@@ -17,7 +17,6 @@
"build": { "build": {
"builder": "@angular-builders/custom-webpack:browser", "builder": "@angular-builders/custom-webpack:browser",
"options": { "options": {
"extractCss": true,
"preserveSymlinks": true, "preserveSymlinks": true,
"customWebpackConfig": { "customWebpackConfig": {
"path": "./webpack/webpack.browser.ts", "path": "./webpack/webpack.browser.ts",
@@ -67,6 +66,14 @@
"scripts": [] "scripts": []
}, },
"configurations": { "configurations": {
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
},
"production": { "production": {
"fileReplacements": [ "fileReplacements": [
{ {
@@ -76,7 +83,6 @@
], ],
"optimization": true, "optimization": true,
"outputHashing": "all", "outputHashing": "all",
"extractCss": true,
"namedChunks": false, "namedChunks": false,
"aot": true, "aot": true,
"extractLicenses": true, "extractLicenses": true,
@@ -104,6 +110,9 @@
"port": 4000 "port": 4000
}, },
"configurations": { "configurations": {
"development": {
"browserTarget": "dspace-angular:build:development"
},
"production": { "production": {
"browserTarget": "dspace-angular:build:production" "browserTarget": "dspace-angular:build:production"
} }
@@ -157,19 +166,6 @@
} }
} }
}, },
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"cypress/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": { "e2e": {
"builder": "@cypress/schematic:cypress", "builder": "@cypress/schematic:cypress",
"options": { "options": {
@@ -197,6 +193,10 @@
"tsConfig": "tsconfig.server.json" "tsConfig": "tsconfig.server.json"
}, },
"configurations": { "configurations": {
"development": {
"sourceMap": true,
"optimization": false
},
"production": { "production": {
"sourceMap": false, "sourceMap": false,
"optimization": true, "optimization": true,
@@ -253,12 +253,22 @@
"watch": true, "watch": true,
"headless": false "headless": false
} }
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
} }
} }
} }
}, },
"defaultProject": "dspace-angular", "defaultProject": "dspace-angular",
"cli": { "cli": {
"analytics": false "analytics": false,
"defaultCollection": "@angular-eslint/schematics"
} }
} }

View File

@@ -22,7 +22,7 @@ module.exports = function (config) {
reports: ['html', 'lcovonly', 'text-summary'], reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true fixWebpackSourcePaths: true
}, },
reporters: ['mocha', 'kjhtml'], reporters: ['mocha', 'kjhtml', 'coverage-istanbul'],
mochaReporter: { mochaReporter: {
ignoreSkipped: true, ignoreSkipped: true,
output: 'autowatch' output: 'autowatch'

View File

@@ -9,10 +9,10 @@
"start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"", "start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"",
"start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr", "start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr",
"start:mirador:prod": "yarn run build:mirador && yarn run start:prod", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod",
"serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", "serve": "ng serve -c development",
"serve:ssr": "node dist/server/main", "serve:ssr": "node dist/server/main",
"analyze": "webpack-bundle-analyzer dist/browser/stats.json", "analyze": "webpack-bundle-analyzer dist/browser/stats.json",
"build": "ng build", "build": "ng build -c development",
"build:stats": "ng build --stats-json", "build:stats": "ng build --stats-json",
"build:prod": "yarn run build:ssr", "build:prod": "yarn run build:ssr",
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
@@ -48,32 +48,35 @@
"private": true, "private": true,
"resolutions": { "resolutions": {
"minimist": "^1.2.5", "minimist": "^1.2.5",
"webdriver-manager": "^12.1.8" "webdriver-manager": "^12.1.8",
"ts-node": "10.2.1"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "~11.2.14", "@angular/animations": "~13.2.6",
"@angular/cdk": "^11.2.13", "@angular/cdk": "^13.2.6",
"@angular/common": "~11.2.14", "@angular/common": "~13.2.6",
"@angular/compiler": "~11.2.14", "@angular/compiler": "~13.2.6",
"@angular/core": "~11.2.14", "@angular/core": "~13.2.6",
"@angular/forms": "~11.2.14", "@angular/forms": "~13.2.6",
"@angular/localize": "11.2.14", "@angular/localize": "13.2.6",
"@angular/platform-browser": "~11.2.14", "@angular/platform-browser": "~13.2.6",
"@angular/platform-browser-dynamic": "~11.2.14", "@angular/platform-browser-dynamic": "~13.2.6",
"@angular/platform-server": "~11.2.14", "@angular/platform-server": "~13.2.6",
"@angular/router": "~11.2.14", "@angular/router": "~13.2.6",
"@kolkov/ngx-gallery": "^1.2.3", "@babel/runtime": "^7.17.2",
"@ng-bootstrap/ng-bootstrap": "9.1.3", "@kolkov/ngx-gallery": "^2.0.1",
"@ng-dynamic-forms/core": "^13.0.0", "@material-ui/core": "^4.11.0",
"@ng-dynamic-forms/ui-ng-bootstrap": "^13.0.0", "@material-ui/icons": "^4.9.1",
"@ngrx/effects": "^11.1.1", "@ng-bootstrap/ng-bootstrap": "^11.0.0",
"@ngrx/router-store": "^11.1.1", "@ng-dynamic-forms/core": "^14.0.1",
"@ngrx/store": "^11.1.1", "@ng-dynamic-forms/ui-ng-bootstrap": "^14.0.1",
"@nguniversal/express-engine": "11.2.1", "@ngrx/effects": "^13.0.2",
"@ngrx/router-store": "^13.0.2",
"@ngrx/store": "^13.0.2",
"@nguniversal/express-engine": "^13.0.2",
"@ngx-translate/core": "^13.0.0", "@ngx-translate/core": "^13.0.0",
"@nicky-lenaers/ngx-scroll-to": "^9.0.0", "@nicky-lenaers/ngx-scroll-to": "^9.0.0",
"angular-idle-preload": "3.0.0", "angular-idle-preload": "3.0.0",
"angular2-text-mask": "9.0.0",
"angulartics2": "^10.0.0", "angulartics2": "^10.0.0",
"bootstrap": "4.3.1", "bootstrap": "4.3.1",
"caniuse-lite": "^1.0.30001165", "caniuse-lite": "^1.0.30001165",
@@ -103,7 +106,7 @@
"mirador-share-plugin": "^0.11.0", "mirador-share-plugin": "^0.11.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ng-mocks": "11.11.2", "ng-mocks": "^13.1.1",
"ng2-file-upload": "1.4.0", "ng2-file-upload": "1.4.0",
"ng2-nouislider": "^1.8.3", "ng2-nouislider": "^1.8.3",
"ngx-infinite-scroll": "^10.0.1", "ngx-infinite-scroll": "^10.0.1",
@@ -112,7 +115,9 @@
"ngx-sortablejs": "^11.1.0", "ngx-sortablejs": "^11.1.0",
"nouislider": "^14.6.3", "nouislider": "^14.6.3",
"pem": "1.14.4", "pem": "1.14.4",
"postcss-cli": "^8.3.0", "postcss-cli": "^9.1.0",
"prop-types": "^15.7.2",
"react-copy-to-clipboard": "^5.0.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^6.6.3", "rxjs": "^6.6.3",
"sortablejs": "1.13.0", "sortablejs": "1.13.0",
@@ -120,19 +125,24 @@
"url-parse": "^1.5.6", "url-parse": "^1.5.6",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"webfontloader": "1.6.28", "webfontloader": "1.6.28",
"zone.js": "^0.10.3" "zone.js": "~0.11.5"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "10.0.1", "@angular-builders/custom-webpack": "~13.1.0",
"@angular-devkit/build-angular": "~0.1102.15", "@angular-devkit/build-angular": "~13.2.6",
"@angular/cli": "~11.2.15", "@angular-eslint/builder": "13.1.0",
"@angular/compiler-cli": "~11.2.14", "@angular-eslint/eslint-plugin": "13.1.0",
"@angular/language-service": "~11.2.14", "@angular-eslint/eslint-plugin-template": "13.1.0",
"@angular-eslint/schematics": "13.1.0",
"@angular-eslint/template-parser": "13.1.0",
"@angular/cli": "~13.2.6",
"@angular/compiler-cli": "~13.2.6",
"@angular/language-service": "~13.2.6",
"@cypress/schematic": "^1.5.0", "@cypress/schematic": "^1.5.0",
"@fortawesome/fontawesome-free": "^5.5.0", "@fortawesome/fontawesome-free": "^5.5.0",
"@ngrx/store-devtools": "^11.1.1", "@ngrx/store-devtools": "^13.0.2",
"@ngtools/webpack": "10.2.3", "@ngtools/webpack": "^13.2.6",
"@nguniversal/builders": "~11.2.1", "@nguniversal/builders": "^13.0.2",
"@types/deep-freeze": "0.1.2", "@types/deep-freeze": "0.1.2",
"@types/express": "^4.17.9", "@types/express": "^4.17.9",
"@types/file-saver": "^2.0.1", "@types/file-saver": "^2.0.1",
@@ -141,22 +151,28 @@
"@types/js-cookie": "2.2.6", "@types/js-cookie": "2.2.6",
"@types/lodash": "^4.14.165", "@types/lodash": "^4.14.165",
"@types/node": "^14.14.9", "@types/node": "^14.14.9",
"@typescript-eslint/eslint-plugin": "5.11.0",
"@typescript-eslint/parser": "5.11.0",
"axe-core": "^4.3.3", "axe-core": "^4.3.3",
"codelyzer": "^6.0.0",
"compression-webpack-plugin": "^3.0.1", "compression-webpack-plugin": "^3.0.1",
"copy-webpack-plugin": "^6.4.1", "copy-webpack-plugin": "^6.4.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "3.4.0", "css-loader": "^6.2.0",
"cssnano": "^4.1.10", "css-minimizer-webpack-plugin": "^3.4.1",
"cssnano": "^5.0.6",
"cypress": "9.5.1", "cypress": "9.5.1",
"cypress-axe": "^0.13.0", "cypress-axe": "^0.13.0",
"debug-loader": "^0.0.1", "debug-loader": "^0.0.1",
"deep-freeze": "0.0.1", "deep-freeze": "0.0.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"eslint": "^8.2.0",
"eslint-plugin-deprecation": "^1.3.2",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jsdoc": "^38.0.6",
"eslint-plugin-unused-imports": "^2.0.0",
"fork-ts-checker-webpack-plugin": "^6.0.3", "fork-ts-checker-webpack-plugin": "^6.0.3",
"html-loader": "^1.3.2", "html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.0", "jasmine-core": "^3.8.0",
"jasmine-core": "~3.6.0",
"jasmine-marbles": "0.6.0", "jasmine-marbles": "0.6.0",
"jasmine-spec-reporter": "~5.0.0", "jasmine-spec-reporter": "~5.0.0",
"karma": "^6.3.14", "karma": "^6.3.14",
@@ -165,12 +181,13 @@
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0", "karma-jasmine-html-reporter": "^1.5.0",
"karma-mocha-reporter": "2.2.5", "karma-mocha-reporter": "2.2.5",
"ngx-mask": "^12.0.0",
"nodemon": "^2.0.15", "nodemon": "^2.0.15",
"optimize-css-assets-webpack-plugin": "^5.0.4", "postcss": "^8.1",
"postcss-apply": "0.11.0", "postcss-apply": "0.12.0",
"postcss-import": "^12.0.1", "postcss-import": "^14.0.0",
"postcss-loader": "^3.0.0", "postcss-loader": "^4.0.3",
"postcss-preset-env": "6.7.0", "postcss-preset-env": "^7.4.2",
"postcss-responsive-type": "1.0.0", "postcss-responsive-type": "1.0.0",
"protractor": "^7.0.0", "protractor": "^7.0.0",
"protractor-istanbul-plugin": "2.0.0", "protractor-istanbul-plugin": "2.0.0",
@@ -179,15 +196,15 @@
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs-spy": "^7.5.3", "rxjs-spy": "^7.5.3",
"sass": "~1.32.6",
"sass-loader": "^12.6.0",
"sass-resources-loader": "^2.1.1", "sass-resources-loader": "^2.1.1",
"script-ext-html-webpack-plugin": "2.1.5", "string-replace-loader": "^3.1.0",
"string-replace-loader": "^2.3.0",
"terser-webpack-plugin": "^2.3.1", "terser-webpack-plugin": "^2.3.1",
"ts-loader": "^5.2.0", "ts-loader": "^5.2.0",
"ts-node": "^8.10.2", "ts-node": "^8.10.2",
"tslint": "^6.1.3", "typescript": "~4.5.5",
"typescript": "~4.0.5", "webpack": "^5.69.1",
"webpack": "^4.44.2",
"webpack-bundle-analyzer": "^4.4.0", "webpack-bundle-analyzer": "^4.4.0",
"webpack-cli": "^4.2.0", "webpack-cli": "^4.2.0",
"webpack-dev-server": "^4.5.0" "webpack-dev-server": "^4.5.0"

View File

@@ -15,7 +15,7 @@
* import for `ngExpressEngine`. * import for `ngExpressEngine`.
*/ */
import 'zone.js/dist/zone-node'; import 'zone.js/node';
import 'reflect-metadata'; import 'reflect-metadata';
import 'rxjs'; import 'rxjs';

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -16,7 +17,6 @@ export const EPeopleRegistryActionTypes = {
CANCEL_EDIT_EPERSON: type('dspace/epeople-registry/CANCEL_EDIT_EPERSON'), CANCEL_EDIT_EPERSON: type('dspace/epeople-registry/CANCEL_EDIT_EPERSON'),
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* Used to edit an EPerson in the EPeople registry * Used to edit an EPerson in the EPeople registry
*/ */
@@ -37,7 +37,6 @@ export class EPeopleRegistryCancelEPersonAction implements Action {
type = EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON; type = EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* Export a type alias of all actions in this action group * Export a type alias of all actions in this action group

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { Group } from '../../core/eperson/models/group.model'; import { Group } from '../../core/eperson/models/group.model';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -16,7 +17,6 @@ export const GroupRegistryActionTypes = {
CANCEL_EDIT_GROUP: type('dspace/epeople-registry/CANCEL_EDIT_GROUP'), CANCEL_EDIT_GROUP: type('dspace/epeople-registry/CANCEL_EDIT_GROUP'),
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* Used to edit a Group in the Group registry * Used to edit a Group in the Group registry
*/ */
@@ -37,7 +37,6 @@ export class GroupRegistryCancelGroupAction implements Action {
type = GroupRegistryActionTypes.CANCEL_EDIT_GROUP; type = GroupRegistryActionTypes.CANCEL_EDIT_GROUP;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* Export a type alias of all actions in this action group * Export a type alias of all actions in this action group

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../../shared/ngrx/type'; import { type } from '../../../shared/ngrx/type';
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
@@ -17,7 +18,6 @@ export const BitstreamFormatsRegistryActionTypes = {
DESELECT_ALL_FORMAT: type('dspace/bitstream-formats-registry/DESELECT_ALL_FORMAT') DESELECT_ALL_FORMAT: type('dspace/bitstream-formats-registry/DESELECT_ALL_FORMAT')
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* Used to select a single bitstream format in the bitstream format registry * Used to select a single bitstream format in the bitstream format registry
*/ */
@@ -51,7 +51,6 @@ export class BitstreamFormatsRegistryDeselectAllAction implements Action {
type = BitstreamFormatsRegistryActionTypes.DESELECT_ALL_FORMAT; type = BitstreamFormatsRegistryActionTypes.DESELECT_ALL_FORMAT;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* Export a type alias of all actions in this action group * Export a type alias of all actions in this action group

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../../shared/ngrx/type'; import { type } from '../../../shared/ngrx/type';
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
@@ -26,7 +27,6 @@ export const MetadataRegistryActionTypes = {
DESELECT_ALL_FIELD: type('dspace/metadata-registry/DESELECT_ALL_FIELD') DESELECT_ALL_FIELD: type('dspace/metadata-registry/DESELECT_ALL_FIELD')
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* Used to edit a metadata schema in the metadata registry * Used to edit a metadata schema in the metadata registry
*/ */
@@ -133,7 +133,6 @@ export class MetadataRegistryDeselectAllFieldAction implements Action {
type = MetadataRegistryActionTypes.DESELECT_ALL_FIELD; type = MetadataRegistryActionTypes.DESELECT_ALL_FIELD;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* Export a type alias of all actions in this action group * Export a type alias of all actions in this action group

View File

@@ -52,7 +52,7 @@ describe('MetadataRegistryComponent', () => {
} }
]; ];
const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList)); const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList));
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
const registryServiceStub = { const registryServiceStub = {
getMetadataSchemas: () => mockSchemas, getMetadataSchemas: () => mockSchemas,
getActiveMetadataSchema: () => observableOf(undefined), getActiveMetadataSchema: () => observableOf(undefined),
@@ -66,7 +66,7 @@ describe('MetadataRegistryComponent', () => {
}, },
clearMetadataSchemaRequests: () => observableOf(undefined) clearMetadataSchemaRequests: () => observableOf(undefined)
}; };
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();

View File

@@ -17,7 +17,7 @@ describe('MetadataSchemaFormComponent', () => {
let fixture: ComponentFixture<MetadataSchemaFormComponent>; let fixture: ComponentFixture<MetadataSchemaFormComponent>;
let registryService: RegistryService; let registryService: RegistryService;
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
const registryServiceStub = { const registryServiceStub = {
getActiveMetadataSchema: () => observableOf(undefined), getActiveMetadataSchema: () => observableOf(undefined),
createOrUpdateMetadataSchema: (schema: MetadataSchema) => observableOf(schema), createOrUpdateMetadataSchema: (schema: MetadataSchema) => observableOf(schema),
@@ -33,7 +33,7 @@ describe('MetadataSchemaFormComponent', () => {
}; };
} }
}; };
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({

View File

@@ -24,7 +24,7 @@ describe('MetadataFieldFormComponent', () => {
prefix: 'fake' prefix: 'fake'
}); });
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
const registryServiceStub = { const registryServiceStub = {
getActiveMetadataField: () => observableOf(undefined), getActiveMetadataField: () => observableOf(undefined),
createMetadataField: (field: MetadataField) => observableOf(field), createMetadataField: (field: MetadataField) => observableOf(field),
@@ -43,7 +43,7 @@ describe('MetadataFieldFormComponent', () => {
}; };
} }
}; };
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({

View File

@@ -106,7 +106,7 @@ describe('MetadataSchemaComponent', () => {
} }
]; ];
const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList)); const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList));
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
const registryServiceStub = { const registryServiceStub = {
getMetadataSchemas: () => mockSchemas, getMetadataSchemas: () => mockSchemas,
getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockFieldsList.filter((value) => value.id === 3 || value.id === 4))), getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockFieldsList.filter((value) => value.id === 3 || value.id === 4))),
@@ -122,7 +122,7 @@ describe('MetadataSchemaComponent', () => {
}, },
clearMetadataFieldRequests: () => observableOf(undefined) clearMetadataFieldRequests: () => observableOf(undefined)
}; };
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
const schemaNameParam = 'mock'; const schemaNameParam = 'mock';
const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
params: observableOf({ params: observableOf({

View File

@@ -1,28 +1,30 @@
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary my-1 move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate"> <div class="space-children-mr my-1">
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate">
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span> <i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-secondary my-1 private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate"> <a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-secondary private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate">
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span> <i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-secondary my-1 public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate"> <a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-secondary public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate">
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span> <i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary my-1 edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-secondary edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate">
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span> <i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-warning t my-1 withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate"> <a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-warning t withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate">
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span> <i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-warning my-1 reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate"> <a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-warning reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate">
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span> <i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" class="btn btn-danger my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-danger delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate">
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span> <i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
</a> </a>
</div>

View File

@@ -12,7 +12,7 @@ import { Router } from '@angular/router';
* Represents a non-expandable section in the admin sidebar * Represents a non-expandable section in the admin sidebar
*/ */
@Component({ @Component({
/* tslint:disable:component-selector */ /* eslint-disable @angular-eslint/component-selector */
selector: 'li[ds-admin-sidebar-section]', selector: 'li[ds-admin-sidebar-section]',
templateUrl: './admin-sidebar-section.component.html', templateUrl: './admin-sidebar-section.component.html',
styleUrls: ['./admin-sidebar-section.component.scss'], styleUrls: ['./admin-sidebar-section.component.scss'],

View File

@@ -15,7 +15,7 @@ import { Router } from '@angular/router';
* Represents a expandable section in the sidebar * Represents a expandable section in the sidebar
*/ */
@Component({ @Component({
/* tslint:disable:component-selector */ /* eslint-disable @angular-eslint/component-selector */
selector: 'li[ds-expandable-admin-sidebar-section]', selector: 'li[ds-expandable-admin-sidebar-section]',
templateUrl: './expandable-admin-sidebar-section.component.html', templateUrl: './expandable-admin-sidebar-section.component.html',
styleUrls: ['./expandable-admin-sidebar-section.component.scss'], styleUrls: ['./expandable-admin-sidebar-section.component.scss'],

View File

@@ -1,7 +1,8 @@
<div class="space-children-mr">
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span> <i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
</a> </a>
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate"> <a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate">
<i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span> <i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span>
</a> </a>
</div>

View File

@@ -59,6 +59,7 @@ import { PageInternalServerErrorComponent } from './page-internal-server-error/p
import { ThemedAdminSidebarComponent } from './admin/admin-sidebar/themed-admin-sidebar.component'; import { ThemedAdminSidebarComponent } from './admin/admin-sidebar/themed-admin-sidebar.component';
import { APP_CONFIG, AppConfig } from '../config/app-config.interface'; import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
import { NgxMaskModule } from 'ngx-mask';
export function getConfig() { export function getConfig() {
return environment; return environment;
@@ -90,6 +91,7 @@ const IMPORTS = [
ScrollToModule.forRoot(), ScrollToModule.forRoot(),
NgbModule, NgbModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
NgxMaskModule.forRoot(),
EffectsModule.forRoot(appEffects), EffectsModule.forRoot(appEffects),
StoreModule.forRoot(appReducers, storeModuleConfig), StoreModule.forRoot(appReducers, storeModuleConfig),
StoreRouterConnectingModule.forRoot(), StoreRouterConnectingModule.forRoot(),

View File

@@ -127,10 +127,10 @@ export class BrowseByMetadataPageComponent implements OnInit {
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort]; return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
}) })
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
this.browseId = params.id || this.defaultBrowseId; this.browseId = params.id || this.defaultBrowseId;
this.authority = params.authority; this.authority = params.authority;
this.value = +params.value || params.value || ''; this.value = +params.value || params.value || '';
this.startsWith = +params.startsWith || params.startsWith; this.startsWith = +params.startsWith || params.startsWith;
const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId);
if (isNotEmpty(this.value)) { if (isNotEmpty(this.value)) {
this.updatePageWithItems(searchOptions, this.value, this.authority); this.updatePageWithItems(searchOptions, this.value, this.authority);

View File

@@ -45,7 +45,7 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort]; return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
}) })
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
this.browseId = params.id || this.defaultBrowseId; this.browseId = params.id || this.defaultBrowseId;
this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined); this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined);
this.updateParent(params.scope); this.updateParent(params.scope);
})); }));

View File

@@ -112,15 +112,15 @@ describe('CollectionItemMapperComponent', () => {
}; };
const searchServiceStub = Object.assign(new SearchServiceStub(), { const searchServiceStub = Object.assign(new SearchServiceStub(), {
search: () => observableOf(emptyList), search: () => observableOf(emptyList),
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
clearDiscoveryRequests: () => {} clearDiscoveryRequests: () => {}
/* tslint:enable:no-empty */ /* eslint-enable no-empty,@typescript-eslint/no-empty-function */
}); });
const collectionDataServiceStub = { const collectionDataServiceStub = {
getMappedItems: () => observableOf(emptyList), getMappedItems: () => observableOf(emptyList),
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
clearMappedItemsRequests: () => {} clearMappedItemsRequests: () => {}
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
}; };
const routeServiceStub = { const routeServiceStub = {
getRouteParameterValue: () => { getRouteParameterValue: () => {

View File

@@ -34,7 +34,7 @@
[title]="'collection.page.news'"> [title]="'collection.page.news'">
</ds-comcol-page-content> </ds-comcol-page-content>
</header> </header>
<div class="pl-2"> <div class="pl-2 space-children-mr">
<ds-dso-page-edit-button *ngIf="isCollectionAdmin$ | async" [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button *ngIf="isCollectionAdmin$ | async" [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>

View File

@@ -5,11 +5,11 @@
<h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2> <h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2>
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dso.name } }}</p> <p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dso.name } }}</p>
<div class="form-group row"> <div class="form-group row">
<div class="col text-right"> <div class="col text-right space-children-mr">
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)"> <button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">
<i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}} <i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}}
</button> </button>
<button class="btn btn-danger mr-2" (click)="onConfirm(dso)" [disabled]="(processing$ | async)"> <button class="btn btn-danger" (click)="onConfirm(dso)" [disabled]="(processing$ | async)">
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'collection.delete.processing' | translate}}</span> <span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'collection.delete.processing' | translate}}</span>
<span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'collection.delete.confirm' | translate}}</span> <span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'collection.delete.confirm' | translate}}</span>
</button> </button>

View File

@@ -1,6 +1,6 @@
<div class="container-fluid mb-2" *ngVar="(itemTemplateRD$ | async) as itemTemplateRD"> <div class="container-fluid mb-2" *ngVar="(itemTemplateRD$ | async) as itemTemplateRD">
<label>{{ 'collection.edit.template.label' | translate}}</label> <label>{{ 'collection.edit.template.label' | translate}}</label>
<div class="button-row"> <div class="button-row space-children-mr">
<button *ngIf="!itemTemplateRD?.payload" class="btn btn-success" (click)="addItemTemplate()"> <button *ngIf="!itemTemplateRD?.payload" class="btn btn-success" (click)="addItemTemplate()">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline">&nbsp;{{"collection.edit.template.add-button" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"collection.edit.template.add-button" | translate}}</span>

View File

@@ -1,5 +1,5 @@
<div *ngVar="(contentSource$ |async) as contentSource"> <div *ngVar="(contentSource$ |async) as contentSource">
<div class="container-fluid" *ngIf="shouldShow"> <div class="container-fluid space-children-mr" *ngIf="shouldShow">
<h4>{{ 'collection.source.controls.head' | translate }}</h4> <h4>{{ 'collection.source.controls.head' | translate }}</h4>
<div> <div>
<span class="font-weight-bold">{{'collection.source.controls.harvest.status' | translate}}</span> <span class="font-weight-bold">{{'collection.source.controls.harvest.status' | translate}}</span>

View File

@@ -1,5 +1,5 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="d-inline-block float-right"> <div class="d-inline-block float-right space-children-mr">
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)" <button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
[disabled]="!(hasChanges() | async)" [disabled]="!(hasChanges() | async)"
(click)="discard()"><i (click)="discard()"><i
@@ -43,7 +43,7 @@
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)"> <div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="d-inline-block float-right ml-1"> <div class="d-inline-block float-right ml-1 space-children-mr">
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)" <button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
[disabled]="!(hasChanges() | async)" [disabled]="!(hasChanges() | async)"
(click)="discard()"><i (click)="discard()"><i

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { createSelector, Store } from '@ngrx/store'; import { createSelector, Store } from '@ngrx/store';
@@ -85,7 +86,6 @@ export const MAX_COMCOLS_PER_PAGE = 20;
* Service class for the community list, responsible for the creating of the flat list used by communityList dataSource * Service class for the community list, responsible for the creating of the flat list used by communityList dataSource
* and connection to the store to retrieve and save the state of the community list * and connection to the store to retrieve and save the state of the community list
*/ */
// tslint:disable-next-line:max-classes-per-file
@Injectable() @Injectable()
export class CommunityListService { export class CommunityListService {

View File

@@ -8,7 +8,7 @@
<span class="fa fa-chevron-right invisible" aria-hidden="true"></span> <span class="fa fa-chevron-right invisible" aria-hidden="true"></span>
</button> </button>
<div class="align-middle pt-2"> <div class="align-middle pt-2">
<a *ngIf="node!==loadingNode" [routerLink]="" (click)="getNextPage(node)" <a *ngIf="node!==loadingNode" [routerLink]="[]" (click)="getNextPage(node)"
class="btn btn-outline-primary btn-sm" role="button"> class="btn btn-outline-primary btn-sm" role="button">
<i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }} <i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }}
</a> </a>

View File

@@ -7,7 +7,8 @@ import { Component } from '@angular/core';
selector: 'ds-themed-community-list', selector: 'ds-themed-community-list',
styleUrls: [], styleUrls: [],
templateUrl: '../../shared/theme-support/themed.component.html', templateUrl: '../../shared/theme-support/themed.component.html',
})export class ThemedCommunityListComponent extends ThemedComponent<CommunityListComponent> { })
export class ThemedCommunityListComponent extends ThemedComponent<CommunityListComponent> {
protected getComponentName(): string { protected getComponentName(): string {
return 'CommunityListComponent'; return 'CommunityListComponent';
} }

View File

@@ -20,7 +20,7 @@
[title]="'community.page.news'"> [title]="'community.page.news'">
</ds-comcol-page-content> </ds-comcol-page-content>
</header> </header>
<div class="pl-2"> <div class="pl-2 space-children-mr">
<ds-dso-page-edit-button *ngIf="isCommunityAdmin$ | async" [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button *ngIf="isCommunityAdmin$ | async" [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>

View File

@@ -5,11 +5,11 @@
<h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2> <h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2>
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dso.name } }}</p> <p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dso.name } }}</p>
<div class="form-group row"> <div class="form-group row">
<div class="col text-right"> <div class="col text-right space-children-mr">
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)"> <button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">
<i class="fas fa-times"></i> {{'community.delete.cancel' | translate}} <i class="fas fa-times"></i> {{'community.delete.cancel' | translate}}
</button> </button>
<button class="btn btn-danger mr-2" (click)="onConfirm(dso)" [disabled]="(processing$ | async)"> <button class="btn btn-danger" (click)="onConfirm(dso)" [disabled]="(processing$ | async)">
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'community.delete.processing' | translate}}</span> <span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'community.delete.processing' | translate}}</span>
<span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'community.delete.confirm' | translate}}</span> <span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'community.delete.confirm' | translate}}</span>
</button> </button>

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
// import @ngrx // import @ngrx
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
// import type function // import type function
@@ -39,7 +40,6 @@ export const AuthActionTypes = {
UNSET_USER_AS_IDLE: type('dspace/auth/UNSET_USER_AS_IDLE') UNSET_USER_AS_IDLE: type('dspace/auth/UNSET_USER_AS_IDLE')
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* Authenticate. * Authenticate.
@@ -411,7 +411,6 @@ export class SetUserAsIdleAction implements Action {
export class UnsetUserAsIdleAction implements Action { export class UnsetUserAsIdleAction implements Action {
public type: string = AuthActionTypes.UNSET_USER_AS_IDLE; public type: string = AuthActionTypes.UNSET_USER_AS_IDLE;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* Actions type. * Actions type.

View File

@@ -1,4 +1,4 @@
import { fakeAsync, flush, TestBed } from '@angular/core/testing'; import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing'; import { provideMockActions } from '@ngrx/effects/testing';
import { Store, StoreModule } from '@ngrx/store'; import { Store, StoreModule } from '@ngrx/store';
@@ -396,44 +396,43 @@ describe('AuthEffects', () => {
}); });
describe('when auth loaded is false', () => { describe('when auth loaded is false', () => {
it('should not call removeToken method', (done) => { it('should not call removeToken method', fakeAsync(() => {
store.overrideSelector(isAuthenticatedLoaded, false); store.overrideSelector(isAuthenticatedLoaded, false);
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); actions = observableOf({ type: StoreActionTypes.REHYDRATE });
spyOn(authServiceStub, 'removeToken'); spyOn(authServiceStub, 'removeToken');
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => { authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
expect(false).toBeTrue(); // subscribe to trigger taps, fail if the effect emits (we don't expect it to)
});
tick(1000);
expect(authServiceStub.removeToken).not.toHaveBeenCalled(); expect(authServiceStub.removeToken).not.toHaveBeenCalled();
}));
});
done();
});
}); });
describe('when auth loaded is true', () => { describe('when auth loaded is true', () => {
it('should call removeToken method', fakeAsync(() => { it('should call removeToken method', (done) => {
spyOn(console, 'log').and.callThrough();
store.overrideSelector(isAuthenticatedLoaded, true); store.overrideSelector(isAuthenticatedLoaded, true);
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); actions = observableOf({ type: StoreActionTypes.REHYDRATE });
spyOn(authServiceStub, 'removeToken'); spyOn(authServiceStub, 'removeToken');
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => { authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
expect(authServiceStub.removeToken).toHaveBeenCalled(); expect(authServiceStub.removeToken).toHaveBeenCalled();
flush(); done();
});
}); });
}));
}); });
}); });
describe('invalidateAuthorizationsRequestCache$', () => { describe('invalidateAuthorizationsRequestCache$', () => {
it('should call invalidateAuthorizationsRequestCache method in response to a REHYDRATE action', (done) => { it('should call invalidateAuthorizationsRequestCache method in response to a REHYDRATE action', (done) => {
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); actions = observableOf({ type: StoreActionTypes.REHYDRATE });
authEffects.invalidateAuthorizationsRequestCache$.subscribe(() => { authEffects.invalidateAuthorizationsRequestCache$.subscribe(() => {
expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled(); expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled();
});
done(); done();
}); });
}); });
}); });
});

View File

@@ -10,7 +10,7 @@ import {
} from 'rxjs'; } from 'rxjs';
import { catchError, filter, map, observeOn, switchMap, take, tap } from 'rxjs/operators'; import { catchError, filter, map, observeOn, switchMap, take, tap } from 'rxjs/operators';
// import @ngrx // import @ngrx
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Action, select, Store } from '@ngrx/store'; import { Action, select, Store } from '@ngrx/store';
// import services // import services
@@ -67,8 +67,7 @@ export class AuthEffects {
* Authenticate user. * Authenticate user.
* @method authenticate * @method authenticate
*/ */
@Effect() public authenticate$: Observable<Action> = createEffect(() => this.actions$.pipe(
public authenticate$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.AUTHENTICATE), ofType(AuthActionTypes.AUTHENTICATE),
switchMap((action: AuthenticateAction) => { switchMap((action: AuthenticateAction) => {
return this.authService.authenticate(action.payload.email, action.payload.password).pipe( return this.authService.authenticate(action.payload.email, action.payload.password).pipe(
@@ -77,26 +76,23 @@ export class AuthEffects {
catchError((error) => observableOf(new AuthenticationErrorAction(error))) catchError((error) => observableOf(new AuthenticationErrorAction(error)))
); );
}) })
); ));
@Effect() public authenticateSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
public authenticateSuccess$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.AUTHENTICATE_SUCCESS), ofType(AuthActionTypes.AUTHENTICATE_SUCCESS),
map((action: AuthenticationSuccessAction) => new AuthenticatedAction(action.payload)) map((action: AuthenticationSuccessAction) => new AuthenticatedAction(action.payload))
); ));
@Effect() public authenticated$: Observable<Action> = createEffect(() => this.actions$.pipe(
public authenticated$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.AUTHENTICATED), ofType(AuthActionTypes.AUTHENTICATED),
switchMap((action: AuthenticatedAction) => { switchMap((action: AuthenticatedAction) => {
return this.authService.authenticatedUser(action.payload).pipe( return this.authService.authenticatedUser(action.payload).pipe(
map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)), map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)),
catchError((error) => observableOf(new AuthenticatedErrorAction(error))),); catchError((error) => observableOf(new AuthenticatedErrorAction(error))),);
}) })
); ));
@Effect() public authenticatedSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
public authenticatedSuccess$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.AUTHENTICATED_SUCCESS), ofType(AuthActionTypes.AUTHENTICATED_SUCCESS),
tap((action: AuthenticatedSuccessAction) => this.authService.storeToken(action.payload.authToken)), tap((action: AuthenticatedSuccessAction) => this.authService.storeToken(action.payload.authToken)),
switchMap((action: AuthenticatedSuccessAction) => this.authService.getRedirectUrl().pipe( switchMap((action: AuthenticatedSuccessAction) => this.authService.getRedirectUrl().pipe(
@@ -110,26 +106,23 @@ export class AuthEffects {
return new RetrieveAuthenticatedEpersonAction(action.payload.userHref); return new RetrieveAuthenticatedEpersonAction(action.payload.userHref);
} }
}) })
); ));
@Effect({ dispatch: false }) public redirectAfterLoginSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
public redirectAfterLoginSuccess$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS), ofType(AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS),
tap((action: RedirectAfterLoginSuccessAction) => { tap((action: RedirectAfterLoginSuccessAction) => {
this.authService.clearRedirectUrl(); this.authService.clearRedirectUrl();
this.authService.navigateToRedirectUrl(action.payload); this.authService.navigateToRedirectUrl(action.payload);
}) })
); ), { dispatch: false });
// It means "reacts to this action but don't send another" // It means "reacts to this action but don't send another"
@Effect({ dispatch: false }) public authenticatedError$: Observable<Action> = createEffect(() => this.actions$.pipe(
public authenticatedError$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.AUTHENTICATED_ERROR), ofType(AuthActionTypes.AUTHENTICATED_ERROR),
tap((action: LogOutSuccessAction) => this.authService.removeToken()) tap((action: LogOutSuccessAction) => this.authService.removeToken())
); ), { dispatch: false });
@Effect() public retrieveAuthenticatedEperson$: Observable<Action> = createEffect(() => this.actions$.pipe(
public retrieveAuthenticatedEperson$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON), ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON),
switchMap((action: RetrieveAuthenticatedEpersonAction) => { switchMap((action: RetrieveAuthenticatedEpersonAction) => {
const impersonatedUserID = this.authService.getImpersonateID(); const impersonatedUserID = this.authService.getImpersonateID();
@@ -143,20 +136,18 @@ export class AuthEffects {
map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)), map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)),
catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error)))); catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error))));
}) })
); ));
@Effect() public checkToken$: Observable<Action> = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN),
public checkToken$: Observable<Action> = this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN),
switchMap(() => { switchMap(() => {
return this.authService.hasValidAuthenticationToken().pipe( return this.authService.hasValidAuthenticationToken().pipe(
map((token: AuthTokenInfo) => new AuthenticatedAction(token)), map((token: AuthTokenInfo) => new AuthenticatedAction(token)),
catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction())) catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction()))
); );
}) })
); ));
@Effect() public checkTokenCookie$: Observable<Action> = createEffect(() => this.actions$.pipe(
public checkTokenCookie$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE), ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE),
switchMap(() => { switchMap(() => {
return this.authService.checkAuthenticationCookie().pipe( return this.authService.checkAuthenticationCookie().pipe(
@@ -171,10 +162,9 @@ export class AuthEffects {
catchError((error) => observableOf(new AuthenticatedErrorAction(error))) catchError((error) => observableOf(new AuthenticatedErrorAction(error)))
); );
}) })
); ));
@Effect() public retrieveToken$: Observable<Action> = createEffect(() => this.actions$.pipe(
public retrieveToken$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.RETRIEVE_TOKEN), ofType(AuthActionTypes.RETRIEVE_TOKEN),
switchMap((action: AuthenticateAction) => { switchMap((action: AuthenticateAction) => {
return this.authService.refreshAuthenticationToken(null).pipe( return this.authService.refreshAuthenticationToken(null).pipe(
@@ -183,55 +173,51 @@ export class AuthEffects {
catchError((error) => observableOf(new AuthenticationErrorAction(error))) catchError((error) => observableOf(new AuthenticationErrorAction(error)))
); );
}) })
); ));
@Effect() public refreshToken$: Observable<Action> = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.REFRESH_TOKEN),
public refreshToken$: Observable<Action> = this.actions$.pipe(ofType(AuthActionTypes.REFRESH_TOKEN),
switchMap((action: RefreshTokenAction) => { switchMap((action: RefreshTokenAction) => {
return this.authService.refreshAuthenticationToken(action.payload).pipe( return this.authService.refreshAuthenticationToken(action.payload).pipe(
map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)), map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)),
catchError((error) => observableOf(new RefreshTokenErrorAction())) catchError((error) => observableOf(new RefreshTokenErrorAction()))
); );
}) })
); ));
// It means "reacts to this action but don't send another" // It means "reacts to this action but don't send another"
@Effect({ dispatch: false }) public refreshTokenSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
public refreshTokenSuccess$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.REFRESH_TOKEN_SUCCESS), ofType(AuthActionTypes.REFRESH_TOKEN_SUCCESS),
tap((action: RefreshTokenSuccessAction) => this.authService.replaceToken(action.payload)) tap((action: RefreshTokenSuccessAction) => this.authService.replaceToken(action.payload))
); ), { dispatch: false });
/** /**
* When the store is rehydrated in the browser, * When the store is rehydrated in the browser,
* clear a possible invalid token or authentication errors * clear a possible invalid token or authentication errors
*/ */
@Effect({ dispatch: false }) public clearInvalidTokenOnRehydrate$: Observable<any> = createEffect(() => this.actions$.pipe(
public clearInvalidTokenOnRehydrate$: Observable<any> = this.actions$.pipe(
ofType(StoreActionTypes.REHYDRATE), ofType(StoreActionTypes.REHYDRATE),
switchMap(() => { switchMap(() => {
const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded)); const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded));
const authenticated$ = this.store.pipe(select(isAuthenticated)); const authenticated$ = this.store.pipe(select(isAuthenticated));
return observableCombineLatest(isLoaded$, authenticated$).pipe( return observableCombineLatest([isLoaded$, authenticated$]).pipe(
take(1), take(1),
filter(([loaded, authenticated]) => loaded && !authenticated), filter(([loaded, authenticated]) => loaded && !authenticated),
tap(() => this.authService.removeToken()), tap(() => this.authService.removeToken()),
tap(() => this.authService.resetAuthenticationError()) tap(() => this.authService.resetAuthenticationError())
); );
})); })), { dispatch: false });
/** /**
* When the store is rehydrated in the browser, invalidate all cache hits regarding the * When the store is rehydrated in the browser, invalidate all cache hits regarding the
* authorizations endpoint, to be sure to have consistent responses after a login with external idp * authorizations endpoint, to be sure to have consistent responses after a login with external idp
* *
*/ */
@Effect({ dispatch: false }) invalidateAuthorizationsRequestCache$ = this.actions$ invalidateAuthorizationsRequestCache$ = createEffect(() => this.actions$
.pipe(ofType(StoreActionTypes.REHYDRATE), .pipe(ofType(StoreActionTypes.REHYDRATE),
tap(() => this.authorizationsService.invalidateAuthorizationsRequestCache()) tap(() => this.authorizationsService.invalidateAuthorizationsRequestCache())
); ), { dispatch: false });
@Effect() public logOut$: Observable<Action> = createEffect(() => this.actions$
public logOut$: Observable<Action> = this.actions$
.pipe( .pipe(
ofType(AuthActionTypes.LOG_OUT), ofType(AuthActionTypes.LOG_OUT),
switchMap(() => { switchMap(() => {
@@ -241,26 +227,23 @@ export class AuthEffects {
catchError((error) => observableOf(new LogOutErrorAction(error))) catchError((error) => observableOf(new LogOutErrorAction(error)))
); );
}) })
); ));
@Effect({ dispatch: false }) public logOutSuccess$: Observable<Action> = createEffect(() => this.actions$
public logOutSuccess$: Observable<Action> = this.actions$
.pipe(ofType(AuthActionTypes.LOG_OUT_SUCCESS), .pipe(ofType(AuthActionTypes.LOG_OUT_SUCCESS),
tap(() => this.authService.removeToken()), tap(() => this.authService.removeToken()),
tap(() => this.authService.clearRedirectUrl()), tap(() => this.authService.clearRedirectUrl()),
tap(() => this.authService.refreshAfterLogout()) tap(() => this.authService.refreshAfterLogout())
); ), { dispatch: false });
@Effect({ dispatch: false }) public redirectToLoginTokenExpired$: Observable<Action> = createEffect(() => this.actions$
public redirectToLoginTokenExpired$: Observable<Action> = this.actions$
.pipe( .pipe(
ofType(AuthActionTypes.REDIRECT_TOKEN_EXPIRED), ofType(AuthActionTypes.REDIRECT_TOKEN_EXPIRED),
tap(() => this.authService.removeToken()), tap(() => this.authService.removeToken()),
tap(() => this.authService.redirectToLoginWhenTokenExpired()) tap(() => this.authService.redirectToLoginWhenTokenExpired())
); ), { dispatch: false });
@Effect() public retrieveMethods$: Observable<Action> = createEffect(() => this.actions$
public retrieveMethods$: Observable<Action> = this.actions$
.pipe( .pipe(
ofType(AuthActionTypes.RETRIEVE_AUTH_METHODS), ofType(AuthActionTypes.RETRIEVE_AUTH_METHODS),
switchMap((action: RetrieveAuthMethodsAction) => { switchMap((action: RetrieveAuthMethodsAction) => {
@@ -270,7 +253,7 @@ export class AuthEffects {
catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction())) catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction()))
); );
}) })
); ));
/** /**
* For any action that is not in {@link IDLE_TIMER_IGNORE_TYPES} that comes in => Start the idleness timer * For any action that is not in {@link IDLE_TIMER_IGNORE_TYPES} that comes in => Start the idleness timer
@@ -278,8 +261,7 @@ export class AuthEffects {
* => Return the action to set the user as idle ({@link SetUserAsIdleAction}) * => Return the action to set the user as idle ({@link SetUserAsIdleAction})
* @method trackIdleness * @method trackIdleness
*/ */
@Effect() public trackIdleness$: Observable<Action> = createEffect(() => this.actions$.pipe(
public trackIdleness$: Observable<Action> = this.actions$.pipe(
filter((action: Action) => !IDLE_TIMER_IGNORE_TYPES.includes(action.type)), filter((action: Action) => !IDLE_TIMER_IGNORE_TYPES.includes(action.type)),
// Using switchMap the effect will stop subscribing to the previous timer if a new action comes // Using switchMap the effect will stop subscribing to the previous timer if a new action comes
// in, and start a new timer // in, and start a new timer
@@ -290,7 +272,7 @@ export class AuthEffects {
// Re-enter the zone to dispatch the action // Re-enter the zone to dispatch the action
observeOn(new EnterZoneScheduler(this.zone, queueScheduler)), observeOn(new EnterZoneScheduler(this.zone, queueScheduler)),
map(() => new SetUserAsIdleAction()), map(() => new SetUserAsIdleAction()),
); ));
/** /**
* @constructor * @constructor

View File

@@ -20,9 +20,9 @@ describe(`AuthInterceptor`, () => {
const authServiceStub = new AuthServiceStub(); const authServiceStub = new AuthServiceStub();
const store: Store<TruncatablesState> = jasmine.createSpyObj('store', { const store: Store<TruncatablesState> = jasmine.createSpyObj('store', {
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
dispatch: {}, dispatch: {},
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
select: observableOf(true) select: observableOf(true)
}); });

View File

@@ -144,7 +144,7 @@ export class AuthInterceptor implements HttpInterceptor {
const regex = /(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g; const regex = /(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g;
const realms = completeWWWauthenticateHeader.match(regex); const realms = completeWWWauthenticateHeader.match(regex);
// tslint:disable-next-line:forin // eslint-disable-next-line guard-for-in
for (const j in realms) { for (const j in realms) {
const splittedRealm = realms[j].split(', '); const splittedRealm = realms[j].split(', ');

View File

@@ -8,6 +8,8 @@ import { createSelector } from '@ngrx/store';
*/ */
import { AuthState } from './auth.reducer'; import { AuthState } from './auth.reducer';
import { AppState } from '../../app.reducer'; import { AppState } from '../../app.reducer';
import { CoreState } from '../core.reducers';
import { coreSelector } from '../core.selectors';
/** /**
* Returns the user state. * Returns the user state.
@@ -15,7 +17,7 @@ import { AppState } from '../../app.reducer';
* @param {AppState} state Top level state. * @param {AppState} state Top level state.
* @return {AuthState} * @return {AuthState}
*/ */
export const getAuthState = (state: any) => state.core.auth; export const getAuthState = createSelector(coreSelector, (state: CoreState) => state.auth);
/** /**
* Returns true if the user is authenticated. * Returns true if the user is authenticated.

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { dataService } from '../cache/builders/build-decorators'; import { dataService } from '../cache/builders/build-decorators';
import { BROWSE_DEFINITION } from '../shared/browse-definition.resource-type'; import { BROWSE_DEFINITION } from '../shared/browse-definition.resource-type';
@@ -18,7 +19,6 @@ import { PaginatedList } from '../data/paginated-list.model';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
/* tslint:disable:max-classes-per-file */
class DataServiceImpl extends DataService<BrowseDefinition> { class DataServiceImpl extends DataService<BrowseDefinition> {
protected linkPath = 'browses'; protected linkPath = 'browses';
@@ -123,4 +123,3 @@ export class BrowseDefinitionDataService {
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,9 +1,9 @@
/* eslint-disable max-classes-per-file */
import { HALLink } from '../../shared/hal-link.model'; import { HALLink } from '../../shared/hal-link.model';
import { HALResource } from '../../shared/hal-resource.model'; import { HALResource } from '../../shared/hal-resource.model';
import { ResourceType } from '../../shared/resource-type'; import { ResourceType } from '../../shared/resource-type';
import { dataService, getDataServiceFor, getLinkDefinition, link, } from './build-decorators'; import { dataService, getDataServiceFor, getLinkDefinition, link, } from './build-decorators';
/* tslint:disable:max-classes-per-file */
class TestService { class TestService {
} }
@@ -80,4 +80,3 @@ describe('build decorators', () => {
}); });
}); });
}); });
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
@@ -12,7 +13,6 @@ import { FindListOptions } from '../../data/find-list-options.model';
const TEST_MODEL = new ResourceType('testmodel'); const TEST_MODEL = new ResourceType('testmodel');
let result: any; let result: any;
/* tslint:disable:max-classes-per-file */
class TestModel implements HALResource { class TestModel implements HALResource {
static type = TEST_MODEL; static type = TEST_MODEL;
@@ -251,4 +251,3 @@ describe('LinkService', () => {
}); });
}); });
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -15,7 +16,6 @@ export const ObjectCacheActionTypes = {
APPLY_PATCH: type('dspace/core/cache/object/APPLY_PATCH') APPLY_PATCH: type('dspace/core/cache/object/APPLY_PATCH')
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to add an object to the cache * An ngrx action to add an object to the cache
*/ */
@@ -126,7 +126,6 @@ export class ApplyPatchObjectCacheAction implements Action {
} }
} }
/* tslint:enable:max-classes-per-file */
/** /**
* A type to encompass all ObjectCacheActions * A type to encompass all ObjectCacheActions

View File

@@ -1,6 +1,6 @@
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { StoreActionTypes } from '../../store.actions'; import { StoreActionTypes } from '../../store.actions';
import { ResetObjectCacheTimestampsAction } from './object-cache.actions'; import { ResetObjectCacheTimestampsAction } from './object-cache.actions';
@@ -16,10 +16,10 @@ export class ObjectCacheEffects {
* This assumes that the server cached everything a negligible * This assumes that the server cached everything a negligible
* time ago, and will likely need to be revisited later * time ago, and will likely need to be revisited later
*/ */
@Effect() fixTimestampsOnRehydrate = this.actions$ fixTimestampsOnRehydrate = createEffect(() => this.actions$
.pipe(ofType(StoreActionTypes.REHYDRATE), .pipe(ofType(StoreActionTypes.REHYDRATE),
map(() => new ResetObjectCacheTimestampsAction(new Date().getTime())) map(() => new ResetObjectCacheTimestampsAction(new Date().getTime()))
); ));
constructor(private actions$: Actions) { constructor(private actions$: Actions) {
} }

View File

@@ -105,10 +105,10 @@ describe('objectCacheReducer', () => {
const action = new AddToObjectCacheAction(objectToCache, timeCompleted, msToLive, requestUUID, altLink1); const action = new AddToObjectCacheAction(objectToCache, timeCompleted, msToLive, requestUUID, altLink1);
const newState = objectCacheReducer(testState, action); const newState = objectCacheReducer(testState, action);
/* tslint:disable:no-string-literal */ /* eslint-disable @typescript-eslint/dot-notation */
expect(newState[selfLink1].data['foo']).toBe('baz'); expect(newState[selfLink1].data['foo']).toBe('baz');
expect(newState[selfLink1].data['somethingElse']).toBe(true); expect(newState[selfLink1].data['somethingElse']).toBe(true);
/* tslint:enable:no-string-literal */ /* eslint-enable @typescript-eslint/dot-notation */
}); });
it('should perform the ADD action without affecting the previous state', () => { it('should perform the ADD action without affecting the previous state', () => {

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { import {
AddPatchObjectCacheAction, AddPatchObjectCacheAction,
AddToObjectCacheAction, AddToObjectCacheAction,
@@ -84,7 +85,6 @@ export class ObjectCacheEntry implements CacheEntry {
alternativeLinks: string[]; alternativeLinks: string[];
} }
/* tslint:enable:max-classes-per-file */
/** /**
* The ObjectCache State * The ObjectCache State

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { PageInfo } from '../shared/page-info.model'; import { PageInfo } from '../shared/page-info.model';
import { ConfigObject } from '../config/models/config.model'; import { ConfigObject } from '../config/models/config.model';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
@@ -5,7 +6,6 @@ import { HALLink } from '../shared/hal-link.model';
import { UnCacheableObject } from '../shared/uncacheable-object.model'; import { UnCacheableObject } from '../shared/uncacheable-object.model';
import { RequestError } from '../data/request-error.model'; import { RequestError } from '../data/request-error.model';
/* tslint:disable:max-classes-per-file */
export class RestResponse { export class RestResponse {
public toCache = true; public toCache = true;
public timeCompleted: number; public timeCompleted: number;
@@ -140,4 +140,3 @@ export class FilteredDiscoveryQueryResponse extends RestResponse {
super(true, statusCode, statusText); super(true, statusCode, statusText);
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -12,7 +13,6 @@ export const ServerSyncBufferActionTypes = {
EMPTY: type('dspace/core/cache/syncbuffer/EMPTY'), EMPTY: type('dspace/core/cache/syncbuffer/EMPTY'),
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to add a new cached object to the server sync buffer * An ngrx action to add a new cached object to the server sync buffer
@@ -71,7 +71,6 @@ export class EmptySSBAction implements Action {
} }
} }
/* tslint:enable:max-classes-per-file */
/** /**
* A type to encompass all ServerSyncBufferActions * A type to encompass all ServerSyncBufferActions

View File

@@ -83,7 +83,7 @@ describe('ServerSyncBufferEffects', () => {
}); });
it('should return a COMMIT action in response to an ADD action', () => { it('should return a COMMIT action in response to an ADD action', () => {
// tslint:disable-next-line:no-shadowed-variable // eslint-disable-next-line @typescript-eslint/no-shadow
testScheduler.run(({ hot, expectObservable }) => { testScheduler.run(({ hot, expectObservable }) => {
actions = hot('a', { actions = hot('a', {
a: { a: {

View File

@@ -1,6 +1,6 @@
import { delay, exhaustMap, map, switchMap, take } from 'rxjs/operators'; import { delay, exhaustMap, map, switchMap, take } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { coreSelector } from '../core.selectors'; import { coreSelector } from '../core.selectors';
import { import {
AddToSSBAction, AddToSSBAction,
@@ -32,7 +32,7 @@ export class ServerSyncBufferEffects {
* Then dispatch a CommitSSBAction * Then dispatch a CommitSSBAction
* When the delay is running, no new AddToSSBActions are processed in this effect * When the delay is running, no new AddToSSBActions are processed in this effect
*/ */
@Effect() setTimeoutForServerSync = this.actions$ setTimeoutForServerSync = createEffect(() => this.actions$
.pipe( .pipe(
ofType(ServerSyncBufferActionTypes.ADD), ofType(ServerSyncBufferActionTypes.ADD),
exhaustMap((action: AddToSSBAction) => { exhaustMap((action: AddToSSBAction) => {
@@ -42,7 +42,7 @@ export class ServerSyncBufferEffects {
delay(timeoutInSeconds * 1000), delay(timeoutInSeconds * 1000),
); );
}) })
); ));
/** /**
* When a CommitSSBAction is dispatched * When a CommitSSBAction is dispatched
@@ -50,7 +50,7 @@ export class ServerSyncBufferEffects {
* When the list of actions is not empty, also dispatch an EmptySSBAction * When the list of actions is not empty, also dispatch an EmptySSBAction
* When the list is empty dispatch a NO_ACTION placeholder action * When the list is empty dispatch a NO_ACTION placeholder action
*/ */
@Effect() commitServerSyncBuffer = this.actions$ commitServerSyncBuffer = createEffect(() => this.actions$
.pipe( .pipe(
ofType(ServerSyncBufferActionTypes.COMMIT), ofType(ServerSyncBufferActionTypes.COMMIT),
switchMap((action: CommitSSBAction) => { switchMap((action: CommitSSBAction) => {
@@ -86,7 +86,7 @@ export class ServerSyncBufferEffects {
}) })
); );
}) })
); ));
/** /**
* private method to create an ApplyPatchObjectCacheAction based on a cache entry * private method to create an ApplyPatchObjectCacheAction based on a cache entry

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RequestService } from '../data/request.service'; import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -31,7 +32,6 @@ class DataServiceImpl extends DataService<ConfigObject> {
} }
} }
// tslint:disable-next-line:max-classes-per-file
export abstract class ConfigService { export abstract class ConfigService {
/** /**
* A private DataService instance to delegate specific methods to. * A private DataService instance to delegate specific methods to.

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { BaseResponseParsingService } from './base-response-parsing.service'; import { BaseResponseParsingService } from './base-response-parsing.service';
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { GetRequest} from './request.models'; import { GetRequest} from './request.models';
@@ -5,7 +6,6 @@ import { DSpaceObject } from '../shared/dspace-object.model';
import { CacheableObject } from '../cache/cacheable-object.model'; import { CacheableObject } from '../cache/cacheable-object.model';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
/* tslint:disable:max-classes-per-file */
class TestService extends BaseResponseParsingService { class TestService extends BaseResponseParsingService {
toCache = true; toCache = true;
@@ -102,4 +102,3 @@ describe('BaseResponseParsingService', () => {
}); });
}); });
}); });
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
import { Serializer } from '../serializer'; import { Serializer } from '../serializer';
@@ -10,7 +11,6 @@ import { environment } from '../../../environments/environment';
import { CacheableObject } from '../cache/cacheable-object.model'; import { CacheableObject } from '../cache/cacheable-object.model';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
/* tslint:disable:max-classes-per-file */
/** /**
* Return true if halObj has a value for `_links.self` * Return true if halObj has a value for `_links.self`
@@ -180,4 +180,3 @@ export abstract class BaseResponseParsingService {
return statusCode >= 200 && statusCode < 300; return statusCode >= 200 && statusCode < 300;
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -60,7 +60,7 @@ class TestService extends ComColDataService<any> {
} }
} }
// tslint:disable:no-shadowed-variable /* eslint-disable @typescript-eslint/no-shadow */
describe('ComColDataService', () => { describe('ComColDataService', () => {
let service: TestService; let service: TestService;
let requestService: RequestService; let requestService: RequestService;

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@@ -15,7 +16,6 @@ import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { CONFIG_PROPERTY } from '../shared/config-property.resource-type'; import { CONFIG_PROPERTY } from '../shared/config-property.resource-type';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
/* tslint:disable:max-classes-per-file */
class DataServiceImpl extends DataService<ConfigurationProperty> { class DataServiceImpl extends DataService<ConfigurationProperty> {
protected linkPath = 'properties'; protected linkPath = 'properties';
@@ -60,4 +60,3 @@ export class ConfigurationDataService {
return this.dataService.findById(name); return this.dataService.findById(name);
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
@@ -27,7 +28,6 @@ import { FindListOptions } from './find-list-options.model';
const endpoint = 'https://rest.api/core'; const endpoint = 'https://rest.api/core';
/* tslint:disable:max-classes-per-file */
class TestService extends DataService<any> { class TestService extends DataService<any> {
constructor( constructor(
@@ -834,4 +834,3 @@ describe('DataService', () => {
}); });
}); });
}); });
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@@ -18,7 +19,6 @@ import { PaginatedList } from './paginated-list.model';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
/* tslint:disable:max-classes-per-file */
class DataServiceImpl extends DataService<DSpaceObject> { class DataServiceImpl extends DataService<DSpaceObject> {
protected linkPath = 'dso'; protected linkPath = 'dso';
@@ -104,4 +104,3 @@ export class DSpaceObjectDataService {
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
import { Serializer } from '../serializer'; import { Serializer } from '../serializer';
@@ -18,7 +19,6 @@ import { URLCombiner } from '../url-combiner/url-combiner';
import { CacheableObject } from '../cache/cacheable-object.model'; import { CacheableObject } from '../cache/cacheable-object.model';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
/* tslint:disable:max-classes-per-file */
/** /**
* Return true if obj has a value for `_links.self` * Return true if obj has a value for `_links.self`
@@ -271,4 +271,3 @@ export class DspaceRestResponseParsingService implements ResponseParsingService
return statusCode >= 200 && statusCode < 300; return statusCode >= 200 && statusCode < 300;
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -100,7 +100,7 @@ describe('EpersonRegistrationService', () => {
})); }));
}); });
// tslint:disable:no-shadowed-variable /* eslint-disable @typescript-eslint/no-shadow */
it('should use cached responses and /registrations/search/findByToken?', () => { it('should use cached responses and /registrations/search/findByToken?', () => {
testScheduler.run(({ cold, expectObservable }) => { testScheduler.run(({ cold, expectObservable }) => {
rdbService.buildSingle.and.returnValue(cold('a', { a: rd })); rdbService.buildSingle.and.returnValue(cold('a', { a: rd }));

View File

@@ -16,7 +16,8 @@ export class FilteredDiscoveryPageResponseParsingService extends BaseResponsePar
toCache = false; toCache = false;
constructor( constructor(
protected objectCache: ObjectCacheService, protected objectCache: ObjectCacheService,
) { super(); ) {
super();
} }
/** /**

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { DataService } from './data.service'; import { DataService } from './data.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@@ -20,7 +21,6 @@ import { CacheableObject } from '../cache/cacheable-object.model';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
/* tslint:disable:max-classes-per-file */
class DataServiceImpl extends DataService<any> { class DataServiceImpl extends DataService<any> {
// linkPath isn't used if we're only searching by href. // linkPath isn't used if we're only searching by href.
protected linkPath = undefined; protected linkPath = undefined;

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ItemDataService } from './item-data.service'; import { ItemDataService } from './item-data.service';
import { UpdateDataService } from './update-data.service'; import { UpdateDataService } from './update-data.service';
@@ -24,7 +25,6 @@ import { Operation } from 'fast-json-patch';
import { getFirstCompletedRemoteData } from '../shared/operators'; import { getFirstCompletedRemoteData } from '../shared/operators';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
/* tslint:disable:max-classes-per-file */
/** /**
* A custom implementation of the ItemDataService, but for collection item templates * A custom implementation of the ItemDataService, but for collection item templates
* Makes sure to change the endpoint before sending out CRUD requests for the item template * Makes sure to change the endpoint before sending out CRUD requests for the item template
@@ -228,4 +228,3 @@ export class ItemTemplateDataService implements UpdateDataService<Item> {
return this.dataService.getCollectionEndpoint(collectionID); return this.dataService.getCollectionEndpoint(collectionID);
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { type } from '../../../shared/ngrx/type'; import { type } from '../../../shared/ngrx/type';
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { INotification } from '../../../shared/notifications/models/notification.model'; import { INotification } from '../../../shared/notifications/models/notification.model';
@@ -22,7 +23,6 @@ export const ObjectUpdatesActionTypes = {
REMOVE_FIELD: type('dspace/core/cache/object-updates/REMOVE_FIELD') REMOVE_FIELD: type('dspace/core/cache/object-updates/REMOVE_FIELD')
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to initialize a new page's fields in the ObjectUpdates state * An ngrx action to initialize a new page's fields in the ObjectUpdates state
@@ -275,7 +275,6 @@ export class RemoveFieldUpdateAction implements Action {
} }
} }
/* tslint:enable:max-classes-per-file */
/** /**
* A type to encompass all ObjectUpdatesActions * A type to encompass all ObjectUpdatesActions

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { import {
DiscardObjectUpdatesAction, DiscardObjectUpdatesAction,
ObjectUpdatesAction, ObjectUpdatesAction,
@@ -52,7 +52,7 @@ export class ObjectUpdatesEffects {
/** /**
* Effect that makes sure all last fired ObjectUpdatesActions are stored in the map of this service, with the url as their key * Effect that makes sure all last fired ObjectUpdatesActions are stored in the map of this service, with the url as their key
*/ */
@Effect({ dispatch: false }) mapLastActions$ = this.actions$ mapLastActions$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(...Object.values(ObjectUpdatesActionTypes)), ofType(...Object.values(ObjectUpdatesActionTypes)),
map((action: ObjectUpdatesAction) => { map((action: ObjectUpdatesAction) => {
@@ -64,12 +64,12 @@ export class ObjectUpdatesEffects {
this.actionMap$[url].next(action); this.actionMap$[url].next(action);
} }
}) })
); ), { dispatch: false });
/** /**
* Effect that makes sure all last fired NotificationActions are stored in the notification map of this service, with the id as their key * Effect that makes sure all last fired NotificationActions are stored in the notification map of this service, with the id as their key
*/ */
@Effect({ dispatch: false }) mapLastNotificationActions$ = this.actions$ mapLastNotificationActions$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(...Object.values(NotificationsActionTypes)), ofType(...Object.values(NotificationsActionTypes)),
map((action: RemoveNotificationAction) => { map((action: RemoveNotificationAction) => {
@@ -80,7 +80,7 @@ export class ObjectUpdatesEffects {
this.notificationActionMap$[id].next(action); this.notificationActionMap$[id].next(action);
} }
) )
); ), { dispatch: false });
/** /**
* Effect that checks whether the removeAction's notification timeout ends before a user triggers another ObjectUpdatesAction * Effect that checks whether the removeAction's notification timeout ends before a user triggers another ObjectUpdatesAction
@@ -88,7 +88,7 @@ export class ObjectUpdatesEffects {
* When a REINSTATE action is fired during the timeout, a NO_ACTION action will be returned * When a REINSTATE action is fired during the timeout, a NO_ACTION action will be returned
* When any other ObjectUpdatesAction is fired during the timeout, a RemoteObjectUpdatesAction will be returned * When any other ObjectUpdatesAction is fired during the timeout, a RemoteObjectUpdatesAction will be returned
*/ */
@Effect() removeAfterDiscardOrReinstateOnUndo$ = this.actions$ removeAfterDiscardOrReinstateOnUndo$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(ObjectUpdatesActionTypes.DISCARD), ofType(ObjectUpdatesActionTypes.DISCARD),
switchMap((action: DiscardObjectUpdatesAction) => { switchMap((action: DiscardObjectUpdatesAction) => {
@@ -134,7 +134,7 @@ export class ObjectUpdatesEffects {
); );
} }
) )
); ));
constructor(private actions$: Actions, constructor(private actions$: Actions,
private notificationsService: NotificationsService) { private notificationsService: NotificationsService) {

View File

@@ -65,11 +65,11 @@ describe('RelationshipTypeService', () => {
buildList = createSuccessfulRemoteDataObject(createPaginatedList([relationshipType1, relationshipType2])); buildList = createSuccessfulRemoteDataObject(createPaginatedList([relationshipType1, relationshipType2]));
rdbService = getMockRemoteDataBuildService(undefined, observableOf(buildList)); rdbService = getMockRemoteDataBuildService(undefined, observableOf(buildList));
objectCache = Object.assign({ objectCache = Object.assign({
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
remove: () => { remove: () => {
}, },
hasBySelfLinkObservable: () => observableOf(false) hasBySelfLinkObservable: () => observableOf(false)
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
}) as ObjectCacheService; }) as ObjectCacheService;
itemService = undefined; itemService = undefined;

View File

@@ -108,12 +108,12 @@ describe('RelationshipService', () => {
'https://rest.api/core/publication/relationships': relationships$ 'https://rest.api/core/publication/relationships': relationships$
}); });
const objectCache = Object.assign({ const objectCache = Object.assign({
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
remove: () => { remove: () => {
}, },
hasBySelfLinkObservable: () => observableOf(false), hasBySelfLinkObservable: () => observableOf(false),
hasByHref$: () => observableOf(false) hasByHref$: () => observableOf(false)
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
}) as ObjectCacheService; }) as ObjectCacheService;
const itemService = jasmine.createSpyObj('itemService', { const itemService = jasmine.createSpyObj('itemService', {

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
import { HALLink } from '../shared/hal-link.model'; import { HALLink } from '../shared/hal-link.model';
@@ -17,7 +18,6 @@ export const RequestActionTypes = {
REMOVE: type('dspace/core/data/request/REMOVE') REMOVE: type('dspace/core/data/request/REMOVE')
}; };
/* tslint:disable:max-classes-per-file */
export abstract class RequestUpdateAction implements Action { export abstract class RequestUpdateAction implements Action {
abstract type: string; abstract type: string;
lastUpdated: number; lastUpdated: number;
@@ -185,7 +185,6 @@ export class RequestRemoveAction implements Action {
} }
} }
/* tslint:enable:max-classes-per-file */
/** /**
* A type to encompass all RequestActions * A type to encompass all RequestActions

View File

@@ -1,6 +1,6 @@
import { Injectable, Injector } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, filter, map, mergeMap, take } from 'rxjs/operators'; import { catchError, filter, map, mergeMap, take } from 'rxjs/operators';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
@@ -25,7 +25,7 @@ import { RequestEntry } from './request-entry.model';
@Injectable() @Injectable()
export class RequestEffects { export class RequestEffects {
@Effect() execute = this.actions$.pipe( execute = createEffect(() => this.actions$.pipe(
ofType(RequestActionTypes.EXECUTE), ofType(RequestActionTypes.EXECUTE),
mergeMap((action: RequestExecuteAction) => { mergeMap((action: RequestExecuteAction) => {
return this.requestService.getByUUID(action.payload).pipe( return this.requestService.getByUUID(action.payload).pipe(
@@ -54,7 +54,7 @@ export class RequestEffects {
}) })
); );
}) })
); ));
/** /**
* When the store is rehydrated in the browser, set all cache * When the store is rehydrated in the browser, set all cache
@@ -64,10 +64,10 @@ export class RequestEffects {
* This assumes that the server cached everything a negligible * This assumes that the server cached everything a negligible
* time ago, and will likely need to be revisited later * time ago, and will likely need to be revisited later
*/ */
@Effect() fixTimestampsOnRehydrate = this.actions$ fixTimestampsOnRehydrate = createEffect(() => this.actions$
.pipe(ofType(StoreActionTypes.REHYDRATE), .pipe(ofType(StoreActionTypes.REHYDRATE),
map(() => new ResetResponseTimestampsAction(new Date().getTime())) map(() => new ResetResponseTimestampsAction(new Date().getTime()))
); ));
constructor( constructor(
private actions$: Actions, private actions$: Actions,

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { GenericConstructor } from '../shared/generic-constructor'; import { GenericConstructor } from '../shared/generic-constructor';
import { ResponseParsingService } from './parsing.service'; import { ResponseParsingService } from './parsing.service';
import { EndpointMapResponseParsingService } from './endpoint-map-response-parsing.service'; import { EndpointMapResponseParsingService } from './endpoint-map-response-parsing.service';
@@ -12,7 +13,6 @@ import { RestRequestWithResponseParser } from './rest-request-with-response-pars
import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
/* tslint:disable:max-classes-per-file */
// uuid and handle requests have separate endpoints // uuid and handle requests have separate endpoints
export enum IdentifierType { export enum IdentifierType {
@@ -254,5 +254,3 @@ export class TaskDeleteRequest extends DeleteRequest {
export class MyDSpaceRequest extends GetRequest { export class MyDSpaceRequest extends GetRequest {
public responseMsToLive = 10 * 1000; public responseMsToLive = 10 * 1000;
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { import {
RequestAction, RequestAction,
RequestActionTypes, RequestActionTypes,

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { DataService } from './data.service'; import { DataService } from './data.service';
import { Root } from './root.model'; import { Root } from './root.model';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
@@ -11,7 +12,7 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { Observable } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { PaginatedList } from './paginated-list.model'; import { PaginatedList } from './paginated-list.model';
@@ -20,9 +21,7 @@ import { FindListOptions } from './find-list-options.model';
import { DspaceRestService } from '../dspace-rest/dspace-rest.service'; import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model';
import { catchError, map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { of } from 'rxjs/internal/observable/of';
/* tslint:disable:max-classes-per-file */
/** /**
* A private DataService implementation to delegate specific methods to. * A private DataService implementation to delegate specific methods to.
@@ -75,7 +74,7 @@ export class RootDataService {
return this.restService.get(this.halService.getRootHref()).pipe( return this.restService.get(this.halService.getRootHref()).pipe(
catchError((err ) => { catchError((err ) => {
console.error(err); console.error(err);
return of(false); return observableOf(false);
}), }),
map((res: RawRestResponse) => res.statusCode === 200) map((res: RawRestResponse) => res.statusCode === 200)
); );
@@ -132,4 +131,3 @@ export class RootDataService {
this.requestService.setStaleByHrefSubstring(this.halService.getRootHref()); this.requestService.setStaleByHrefSubstring(this.halService.getRootHref());
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -7,7 +8,6 @@ export const HistoryActionTypes = {
GET_HISTORY: type('dspace/history/GET_HISTORY') GET_HISTORY: type('dspace/history/GET_HISTORY')
}; };
/* tslint:disable:max-classes-per-file */
export class AddUrlToHistoryAction implements Action { export class AddUrlToHistoryAction implements Action {
type = HistoryActionTypes.ADD_TO_HISTORY; type = HistoryActionTypes.ADD_TO_HISTORY;
@@ -20,7 +20,6 @@ export class AddUrlToHistoryAction implements Action {
} }
} }
/* tslint:enable:max-classes-per-file */
export type HistoryAction export type HistoryAction
= AddUrlToHistoryAction; = AddUrlToHistoryAction;

View File

@@ -3,6 +3,7 @@ import { AddUrlToHistoryAction, HistoryAction, HistoryActionTypes } from './hist
/** /**
* The auth state. * The auth state.
*/ */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface HistoryState extends Array<string> { export interface HistoryState extends Array<string> {
} }

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -12,7 +13,6 @@ export const IndexActionTypes = {
REMOVE_BY_SUBSTRING: type('dspace/core/index/REMOVE_BY_SUBSTRING') REMOVE_BY_SUBSTRING: type('dspace/core/index/REMOVE_BY_SUBSTRING')
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to add a value to the index * An ngrx action to add a value to the index
*/ */
@@ -86,7 +86,6 @@ export class RemoveFromIndexBySubstringAction implements Action {
} }
} }
/* tslint:enable:max-classes-per-file */
/** /**
* A type to encompass all HrefIndexActions * A type to encompass all HrefIndexActions

View File

@@ -1,6 +1,6 @@
import { filter, map, switchMap, take } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { import {
AddToObjectCacheAction, AddToObjectCacheAction,
@@ -24,7 +24,7 @@ import { CoreState } from '../core-state.model';
@Injectable() @Injectable()
export class UUIDIndexEffects { export class UUIDIndexEffects {
@Effect() addObject$ = this.actions$ addObject$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(ObjectCacheActionTypes.ADD), ofType(ObjectCacheActionTypes.ADD),
filter((action: AddToObjectCacheAction) => hasValue(action.payload.objectToCache.uuid)), filter((action: AddToObjectCacheAction) => hasValue(action.payload.objectToCache.uuid)),
@@ -35,13 +35,13 @@ export class UUIDIndexEffects {
action.payload.objectToCache._links.self.href action.payload.objectToCache._links.self.href
); );
}) })
); ));
/** /**
* Adds an alternative link to an object to the ALTERNATIVE_OBJECT_LINK index * Adds an alternative link to an object to the ALTERNATIVE_OBJECT_LINK index
* When the self link of the objectToCache is not the same as the alternativeLink * When the self link of the objectToCache is not the same as the alternativeLink
*/ */
@Effect() addAlternativeObjectLink$ = this.actions$ addAlternativeObjectLink$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(ObjectCacheActionTypes.ADD), ofType(ObjectCacheActionTypes.ADD),
map((action: AddToObjectCacheAction) => { map((action: AddToObjectCacheAction) => {
@@ -57,9 +57,9 @@ export class UUIDIndexEffects {
return new NoOpAction(); return new NoOpAction();
} }
}) })
); ));
@Effect() removeObject$ = this.actions$ removeObject$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(ObjectCacheActionTypes.REMOVE), ofType(ObjectCacheActionTypes.REMOVE),
map((action: RemoveFromObjectCacheAction) => { map((action: RemoveFromObjectCacheAction) => {
@@ -68,9 +68,9 @@ export class UUIDIndexEffects {
action.payload action.payload
); );
}) })
); ));
@Effect() addRequest$ = this.actions$ addRequest$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(RequestActionTypes.CONFIGURE), ofType(RequestActionTypes.CONFIGURE),
filter((action: RequestConfigureAction) => action.payload.method === RestRequestMethod.GET), filter((action: RequestConfigureAction) => action.payload.method === RestRequestMethod.GET),
@@ -95,7 +95,7 @@ export class UUIDIndexEffects {
)]; )];
return actions; return actions;
}) })
); ));
constructor(private actions$: Actions, private store: Store<CoreState>) { constructor(private actions$: Actions, private store: Store<CoreState>) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -23,7 +24,6 @@ export const JsonPatchOperationsActionTypes = {
DELETE_PENDING_JSON_PATCH_OPERATIONS: type('dspace/core/patch/DELETE_PENDING_JSON_PATCH_OPERATIONS'), DELETE_PENDING_JSON_PATCH_OPERATIONS: type('dspace/core/patch/DELETE_PENDING_JSON_PATCH_OPERATIONS'),
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to commit the current transaction * An ngrx action to commit the current transaction
@@ -269,7 +269,6 @@ export class DeletePendingJsonPatchOperationsAction implements Action {
type = JsonPatchOperationsActionTypes.DELETE_PENDING_JSON_PATCH_OPERATIONS; type = JsonPatchOperationsActionTypes.DELETE_PENDING_JSON_PATCH_OPERATIONS;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* Export a type alias of all actions in this action group * Export a type alias of all actions in this action group

View File

@@ -14,9 +14,9 @@ describe('JsonPatchOperationsEffects test suite', () => {
let jsonPatchOperationsEffects: JsonPatchOperationsEffects; let jsonPatchOperationsEffects: JsonPatchOperationsEffects;
let actions: Observable<any>; let actions: Observable<any>;
const store: Store<JsonPatchOperationsState> = jasmine.createSpyObj('store', { const store: Store<JsonPatchOperationsState> = jasmine.createSpyObj('store', {
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
dispatch: {}, dispatch: {},
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
select: observableOf(true) select: observableOf(true)
}); });
const testJsonPatchResourceType = 'testResourceType'; const testJsonPatchResourceType = 'testResourceType';

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Effect, Actions, ofType } from '@ngrx/effects'; import { createEffect, Actions, ofType } from '@ngrx/effects';
import { import {
CommitPatchOperationsAction, FlushPatchOperationsAction, CommitPatchOperationsAction, FlushPatchOperationsAction,
@@ -17,11 +17,11 @@ export class JsonPatchOperationsEffects {
/** /**
* Dispatches a FlushPatchOperationsAction for every dispatched CommitPatchOperationsAction * Dispatches a FlushPatchOperationsAction for every dispatched CommitPatchOperationsAction
*/ */
@Effect() commit$ = this.actions$.pipe( commit$ = createEffect(() => this.actions$.pipe(
ofType(JsonPatchOperationsActionTypes.COMMIT_JSON_PATCH_OPERATIONS), ofType(JsonPatchOperationsActionTypes.COMMIT_JSON_PATCH_OPERATIONS),
map((action: CommitPatchOperationsAction) => { map((action: CommitPatchOperationsAction) => {
return new FlushPatchOperationsAction(action.payload.resourceType, action.payload.resourceId); return new FlushPatchOperationsAction(action.payload.resourceType, action.payload.resourceId);
})); })));
constructor(private actions$: Actions) {} constructor(private actions$: Actions) {}

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-classes-per-file */
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
// tslint:disable:max-classes-per-file
export const MetaTagTypes = { export const MetaTagTypes = {
ADD: type('dspace/meta-tag/ADD'), ADD: type('dspace/meta-tag/ADD'),
CLEAR: type('dspace/meta-tag/CLEAR') CLEAR: type('dspace/meta-tag/CLEAR')

View File

@@ -3,7 +3,7 @@ import { Meta, Title } from '@angular/platform-browser';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs'; import { Observable, of as observableOf, of } from 'rxjs';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
@@ -23,6 +23,7 @@ import { DSONameService } from '../breadcrumbs/dso-name.service';
import { HardRedirectService } from '../services/hard-redirect.service'; import { HardRedirectService } from '../services/hard-redirect.service';
import { getMockStore } from '@ngrx/store/testing'; import { getMockStore } from '@ngrx/store/testing';
import { AddMetaTagAction, ClearMetaTagAction } from './meta-tag.actions'; import { AddMetaTagAction, ClearMetaTagAction } from './meta-tag.actions';
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
describe('MetadataService', () => { describe('MetadataService', () => {
let metadataService: MetadataService; let metadataService: MetadataService;
@@ -38,6 +39,7 @@ describe('MetadataService', () => {
let rootService: RootDataService; let rootService: RootDataService;
let translateService: TranslateService; let translateService: TranslateService;
let hardRedirectService: HardRedirectService; let hardRedirectService: HardRedirectService;
let authorizationService: AuthorizationDataService;
let router: Router; let router: Router;
let store; let store;
@@ -76,6 +78,9 @@ describe('MetadataService', () => {
hardRedirectService = jasmine.createSpyObj( { hardRedirectService = jasmine.createSpyObj( {
getCurrentOrigin: 'https://request.org', getCurrentOrigin: 'https://request.org',
}); });
authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true)
});
// @ts-ignore // @ts-ignore
store = getMockStore({ initialState }); store = getMockStore({ initialState });
@@ -92,7 +97,8 @@ describe('MetadataService', () => {
undefined, undefined,
rootService, rootService,
store, store,
hardRedirectService hardRedirectService,
authorizationService
); );
}); });
@@ -300,6 +306,24 @@ describe('MetadataService', () => {
}); });
})); }));
describe('bitstream not download allowed', () => {
it('should not have citation_pdf_url', fakeAsync(() => {
(bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$([MockBitstream3]));
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
(metadataService as any).processRouteChange({
data: {
value: {
dso: createSuccessfulRemoteDataObject(ItemMock),
}
}
});
tick();
expect(meta.addTag).not.toHaveBeenCalledWith(jasmine.objectContaining({ name: 'citation_pdf_url' }));
}));
});
describe('no primary Bitstream', () => { describe('no primary Bitstream', () => {
it('should link to first and only Bitstream regardless of format', fakeAsync(() => { it('should link to first and only Bitstream regardless of format', fakeAsync(() => {
(bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$([MockBitstream3])); (bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$([MockBitstream3]));

View File

@@ -18,7 +18,11 @@ import { BitstreamFormat } from '../shared/bitstream-format.model';
import { Bitstream } from '../shared/bitstream.model'; import { Bitstream } from '../shared/bitstream.model';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../shared/operators'; import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
getDownloadableBitstream
} from '../shared/operators';
import { RootDataService } from '../data/root-data.service'; import { RootDataService } from '../data/root-data.service';
import { getBitstreamDownloadRoute } from '../../app-routing-paths'; import { getBitstreamDownloadRoute } from '../../app-routing-paths';
import { BundleDataService } from '../data/bundle-data.service'; import { BundleDataService } from '../data/bundle-data.service';
@@ -32,6 +36,7 @@ import { createSelector, select, Store } from '@ngrx/store';
import { AddMetaTagAction, ClearMetaTagAction } from './meta-tag.actions'; import { AddMetaTagAction, ClearMetaTagAction } from './meta-tag.actions';
import { coreSelector } from '../core.selectors'; import { coreSelector } from '../core.selectors';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
/** /**
* The base selector function to select the metaTag section in the store * The base selector function to select the metaTag section in the store
@@ -82,6 +87,7 @@ export class MetadataService {
private rootService: RootDataService, private rootService: RootDataService,
private store: Store<CoreState>, private store: Store<CoreState>,
private hardRedirectService: HardRedirectService, private hardRedirectService: HardRedirectService,
private authorizationService: AuthorizationDataService
) { ) {
} }
@@ -296,7 +302,6 @@ export class MetadataService {
).pipe( ).pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
switchMap((bundle: Bundle) => switchMap((bundle: Bundle) =>
// First try the primary bitstream // First try the primary bitstream
bundle.primaryBitstream.pipe( bundle.primaryBitstream.pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
@@ -307,13 +312,14 @@ export class MetadataService {
return null; return null;
} }
}), }),
getDownloadableBitstream(this.authorizationService),
// return the bundle as well so we can use it again if there's no primary bitstream // return the bundle as well so we can use it again if there's no primary bitstream
map((bitstream: Bitstream) => [bundle, bitstream]) map((bitstream: Bitstream) => [bundle, bitstream])
) )
), ),
switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => { switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => {
if (hasValue(primaryBitstream)) { if (hasValue(primaryBitstream)) {
// If there was a primary bitstream, emit its link // If there was a downloadable primary bitstream, emit its link
return [getBitstreamDownloadRoute(primaryBitstream)]; return [getBitstreamDownloadRoute(primaryBitstream)];
} else { } else {
// Otherwise consider the regular bitstreams in the bundle // Otherwise consider the regular bitstreams in the bundle
@@ -321,8 +327,8 @@ export class MetadataService {
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
switchMap((bitstreamRd: RemoteData<PaginatedList<Bitstream>>) => { switchMap((bitstreamRd: RemoteData<PaginatedList<Bitstream>>) => {
if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) { if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) {
// If there's only one bitstream in the bundle, emit its link // If there's only one bitstream in the bundle, emit its link if its downloadable
return [getBitstreamDownloadRoute(bitstreamRd.payload.page[0])]; return this.getBitLinkIfDownloadable(bitstreamRd.payload.page[0], bitstreamRd);
} else { } else {
// Otherwise check all bitstreams to see if one matches the format whitelist // Otherwise check all bitstreams to see if one matches the format whitelist
return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); return this.getFirstAllowedFormatBitstreamLink(bitstreamRd);
@@ -342,6 +348,20 @@ export class MetadataService {
} }
} }
getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData<PaginatedList<Bitstream>>): Observable<string> {
return observableOf(bitstream).pipe(
getDownloadableBitstream(this.authorizationService),
switchMap((bit: Bitstream) => {
if (hasValue(bit)) {
return [getBitstreamDownloadRoute(bit)];
} else {
// Otherwise check all bitstreams to see if one matches the format whitelist
return this.getFirstAllowedFormatBitstreamLink(bitstreamRd);
}
})
);
}
/** /**
* For Items with more than one Bitstream (and no primary Bitstream), link to the first Bitstream with a MIME type * For Items with more than one Bitstream (and no primary Bitstream), link to the first Bitstream with a MIME type
* included in {@linkcode CITATION_PDF_URL_MIMETYPES} * included in {@linkcode CITATION_PDF_URL_MIMETYPES}
@@ -388,9 +408,14 @@ export class MetadataService {
// for the link at the end // for the link at the end
map((format: BitstreamFormat) => [bitstream, format]) map((format: BitstreamFormat) => [bitstream, format])
)), )),
// Filter out only pairs with whitelisted formats // Check if bitstream downloadable
filter(([, format]: [Bitstream, BitstreamFormat]) => switchMap(([bitstream, format]: [Bitstream, BitstreamFormat]) => observableOf(bitstream).pipe(
hasValue(format) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)), getDownloadableBitstream(this.authorizationService),
map((bit: Bitstream) => [bit, format])
)),
// Filter out only pairs with whitelisted formats and non-null bitstreams, null from download check
filter(([bitstream, format]: [Bitstream, BitstreamFormat]) =>
hasValue(format) && hasValue(bitstream) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)),
// We only need 1 // We only need 1
take(1), take(1),
// Emit the link of the match // Emit the link of the match

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
@@ -28,7 +29,6 @@ import { getFirstCompletedRemoteData } from '../shared/operators';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { FindListOptions } from '../data/find-list-options.model'; import { FindListOptions } from '../data/find-list-options.model';
/* tslint:disable:max-classes-per-file */
/** /**
* A private DataService implementation to delegate specific methods to. * A private DataService implementation to delegate specific methods to.
@@ -222,4 +222,3 @@ export class ResourcePolicyService {
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
@@ -8,7 +9,6 @@ export const RouterActionTypes = {
ROUTE_UPDATE: type('dspace/core/router/ROUTE_UPDATE'), ROUTE_UPDATE: type('dspace/core/router/ROUTE_UPDATE'),
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to be fired when the route is updated * An ngrx action to be fired when the route is updated
* Note that, contrary to the router-store.ROUTER_NAVIGATION action, * Note that, contrary to the router-store.ROUTER_NAVIGATION action,
@@ -19,4 +19,3 @@ export class RouteUpdateAction implements Action {
type = RouterActionTypes.ROUTE_UPDATE; type = RouterActionTypes.ROUTE_UPDATE;
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,6 +1,6 @@
import { filter, map, pairwise } from 'rxjs/operators'; import { filter, map, pairwise } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import * as fromRouter from '@ngrx/router-store'; import * as fromRouter from '@ngrx/router-store';
import { RouterNavigationAction } from '@ngrx/router-store'; import { RouterNavigationAction } from '@ngrx/router-store';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@@ -12,7 +12,7 @@ export class RouterEffects {
* Effect that fires a new RouteUpdateAction when then path of route is changed * Effect that fires a new RouteUpdateAction when then path of route is changed
* @type {Observable<RouteUpdateAction>} * @type {Observable<RouteUpdateAction>}
*/ */
@Effect() routeChange$ = this.actions$ routeChange$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(fromRouter.ROUTER_NAVIGATION), ofType(fromRouter.ROUTER_NAVIGATION),
pairwise(), pairwise(),
@@ -23,7 +23,7 @@ export class RouterEffects {
})), })),
filter((actions: string[]) => actions[0] !== actions[1]), filter((actions: string[]) => actions[0] !== actions[1]),
map(() => new RouteUpdateAction()) map(() => new RouteUpdateAction())
); ));
constructor(private actions$: Actions, private router: Router) { constructor(private actions$: Actions, private router: Router) {
} }

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';
import { Params } from '@angular/router'; import { Params } from '@angular/router';
@@ -15,7 +16,6 @@ export const RouteActionTypes = {
RESET: type('dspace/core/route/RESET'), RESET: type('dspace/core/route/RESET'),
}; };
/* tslint:disable:max-classes-per-file */
/** /**
* An ngrx action to set the query parameters * An ngrx action to set the query parameters
*/ */
@@ -151,7 +151,6 @@ export class ResetRouteStateAction implements Action {
type = RouteActionTypes.RESET; type = RouteActionTypes.RESET;
} }
/* tslint:enable:max-classes-per-file */
/** /**
* A type to encompass all RouteActions * A type to encompass all RouteActions

View File

@@ -1,6 +1,6 @@
import { map, tap } from 'rxjs/operators'; import { map, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { ResetRouteStateAction, RouteActionTypes } from './route.actions'; import { ResetRouteStateAction, RouteActionTypes } from './route.actions';
import { RouterActionTypes } from '../router/router.actions'; import { RouterActionTypes } from '../router/router.actions';
import { RouteService } from './route.service'; import { RouteService } from './route.service';
@@ -11,17 +11,17 @@ export class RouteEffects {
* Effect that resets the route state on reroute * Effect that resets the route state on reroute
* @type {Observable<ResetRouteStateAction>} * @type {Observable<ResetRouteStateAction>}
*/ */
@Effect() routeChange$ = this.actions$ routeChange$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(RouterActionTypes.ROUTE_UPDATE), ofType(RouterActionTypes.ROUTE_UPDATE),
map(() => new ResetRouteStateAction()), map(() => new ResetRouteStateAction()),
); ));
@Effect({dispatch: false }) afterResetChange$ = this.actions$ afterResetChange$ = createEffect(() => this.actions$
.pipe( .pipe(
ofType(RouteActionTypes.RESET), ofType(RouteActionTypes.RESET),
tap(() => this.service.setCurrentRouteInfo()), tap(() => this.service.setCurrentRouteInfo()),
); ), {dispatch: false });
constructor(private actions$: Actions, private service: RouteService) { constructor(private actions$: Actions, private service: RouteService) {
} }

View File

@@ -1,4 +1,5 @@
/** /**
* Represents the payload of a purposefully empty rest response * Represents the payload of a purposefully empty rest response
*/ */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface NoContent {} export interface NoContent {}

View File

@@ -3,6 +3,4 @@
* more details: * more details:
* https://github.com/Microsoft/TypeScript/issues/204#issuecomment-257722306 * https://github.com/Microsoft/TypeScript/issues/204#issuecomment-257722306
*/ */
/* tslint:disable:interface-over-type-literal */
export type GenericConstructor<T> = new (...args: any[]) => T ; export type GenericConstructor<T> = new (...args: any[]) => T ;
/* tslint:enable:interface-over-type-literal */

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-classes-per-file */
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { autoserialize, Serialize, Deserialize } from 'cerialize'; import { autoserialize, Serialize, Deserialize } from 'cerialize';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
/* tslint:disable:max-classes-per-file */
export const VIRTUAL_METADATA_PREFIX = 'virtual::'; export const VIRTUAL_METADATA_PREFIX = 'virtual::';
@@ -138,4 +138,3 @@ export const MetadataMapSerializer = {
return metadataMap; return metadataMap;
} }
}; };
/* tslint:enable:max-classes-per-file */

View File

@@ -17,7 +17,7 @@ import { getRequestFromRequestHref, getRequestFromRequestUUID, getResponseFromEn
import { redirectOn4xx } from './authorized.operators'; import { redirectOn4xx } from './authorized.operators';
import { RequestEntry } from '../data/request-entry.model'; import { RequestEntry } from '../data/request-entry.model';
// tslint:disable:no-shadowed-variable /* eslint-disable @typescript-eslint/no-shadow */
describe('Core Module - RxJS Operators', () => { describe('Core Module - RxJS Operators', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;

View File

@@ -1,4 +1,4 @@
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { debounceTime, filter, find, map, switchMap, take, takeWhile } from 'rxjs/operators'; import { debounceTime, filter, find, map, switchMap, take, takeWhile } from 'rxjs/operators';
import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util'; import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util';
import { SearchResult } from '../../shared/search/models/search-result.model'; import { SearchResult } from '../../shared/search/models/search-result.model';
@@ -9,6 +9,9 @@ import { MetadataSchema } from '../metadata/metadata-schema.model';
import { BrowseDefinition } from './browse-definition.model'; import { BrowseDefinition } from './browse-definition.model';
import { DSpaceObject } from './dspace-object.model'; import { DSpaceObject } from './dspace-object.model';
import { InjectionToken } from '@angular/core'; import { InjectionToken } from '@angular/core';
import { Bitstream } from './bitstream.model';
import { FeatureID } from '../data/feature-authorization/feature-id';
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
export const DEBOUNCE_TIME_OPERATOR = new InjectionToken<<T>(dueTime: number) => (source: Observable<T>) => Observable<T>>('debounceTime', { export const DEBOUNCE_TIME_OPERATOR = new InjectionToken<<T>(dueTime: number) => (source: Observable<T>) => Observable<T>>('debounceTime', {
providedIn: 'root', providedIn: 'root',
@@ -221,3 +224,21 @@ export const metadataFieldsToString = () =>
return fieldSchemaArray.map((fieldSchema: { field: MetadataField, schema: MetadataSchema }) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString()); return fieldSchemaArray.map((fieldSchema: { field: MetadataField, schema: MetadataSchema }) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString());
}) })
); );
/**
* Operator to check if the given bitstream is downloadable
*/
export const getDownloadableBitstream = (authService: AuthorizationDataService) =>
(source: Observable<Bitstream>): Observable<Bitstream | null> =>
source.pipe(
switchMap((bit: Bitstream) => {
if (hasValue(bit)) {
return authService.isAuthorized(FeatureID.CanDownload, bit.self).pipe(
map((canDownload: boolean) => {
return canDownload ? bit : null;
}));
} else {
return observableOf(null);
}
})
);

View File

@@ -30,14 +30,14 @@ describe('SearchFilterService', () => {
const value1 = 'random value'; const value1 = 'random value';
// const value2 = 'another value'; // const value2 = 'another value';
const store: Store<SearchFiltersState> = jasmine.createSpyObj('store', { const store: Store<SearchFiltersState> = jasmine.createSpyObj('store', {
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
dispatch: {}, dispatch: {},
/* tslint:enable:no-empty */ /* eslint-enable no-empty,@typescript-eslint/no-empty-function */
select: observableOf(true) select: observableOf(true)
}); });
const routeServiceStub: any = { const routeServiceStub: any = {
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
hasQueryParamWithValue: (param: string, value: string) => { hasQueryParamWithValue: (param: string, value: string) => {
}, },
hasQueryParam: (param: string) => { hasQueryParam: (param: string) => {
@@ -56,7 +56,7 @@ describe('SearchFilterService', () => {
}, },
getRouteParameterValue: (param: string) => { getRouteParameterValue: (param: string) => {
} }
/* tslint:enable:no-empty */ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
}; };
const activatedRoute: any = new ActivatedRouteStub(); const activatedRoute: any = new ActivatedRouteStub();
const searchServiceStub: any = { const searchServiceStub: any = {

View File

@@ -75,10 +75,10 @@ describe('SearchService', () => {
let routeService; let routeService;
const halService = { const halService = {
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
getEndpoint: () => { getEndpoint: () => {
} }
/* tslint:enable:no-empty */ /* eslint-enable no-empty,@typescript-eslint/no-empty-function */
}; };
@@ -170,10 +170,10 @@ describe('SearchService', () => {
beforeEach(() => { beforeEach(() => {
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint)); spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
spyOn((searchService as any).rdb, 'buildFromHref').and.callThrough(); spyOn((searchService as any).rdb, 'buildFromHref').and.callThrough();
/* tslint:disable:no-empty */ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
searchService.search(searchOptions).subscribe((t) => { searchService.search(searchOptions).subscribe((t) => {
}); // subscribe to make sure all methods are called }); // subscribe to make sure all methods are called
/* tslint:enable:no-empty */ /* eslint-enable no-empty,@typescript-eslint/no-empty-function */
}); });
it('should call getEndpoint on the halService', () => { it('should call getEndpoint on the halService', () => {

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@@ -42,7 +43,6 @@ import { DSOChangeAnalyzer } from '../../data/dso-change-analyzer.service';
import { RestRequest } from '../../data/rest-request.model'; import { RestRequest } from '../../data/rest-request.model';
import { CoreState } from '../../core-state.model'; import { CoreState } from '../../core-state.model';
/* tslint:disable:max-classes-per-file */
/** /**
* A class that lets us delegate some methods to DataService * A class that lets us delegate some methods to DataService
*/ */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
@@ -34,7 +35,6 @@ import { HrefOnlyDataService } from '../../data/href-only-data.service';
import { CoreState } from '../../core-state.model'; import { CoreState } from '../../core-state.model';
import { FindListOptions } from '../../data/find-list-options.model'; import { FindListOptions } from '../../data/find-list-options.model';
/* tslint:disable:max-classes-per-file */
/** /**
* A private DataService implementation to delegate specific methods to. * A private DataService implementation to delegate specific methods to.
@@ -386,4 +386,3 @@ export class VocabularyService {
} }
} }
/* tslint:enable:max-classes-per-file */

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { getTestScheduler } from 'jasmine-marbles'; import { getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
@@ -26,7 +27,6 @@ import { FindListOptions } from '../data/find-list-options.model';
const LINK_NAME = 'test'; const LINK_NAME = 'test';
/* tslint:disable:max-classes-per-file */
class TestTask extends TaskObject { class TestTask extends TaskObject {
} }
@@ -53,7 +53,6 @@ class DummyChangeAnalyzer implements ChangeAnalyzer<TestTask> {
} }
/* tslint:enable:max-classes-per-file */
describe('TasksService', () => { describe('TasksService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;

View File

@@ -90,7 +90,7 @@ export function excludeFromEquals(object: any, propertyName: string): any {
excludedFromEquals.set(object.constructor, [...list, propertyName]); excludedFromEquals.set(object.constructor, [...list, propertyName]);
} }
// tslint:disable-next-line:ban-types // eslint-disable-next-line @typescript-eslint/ban-types
export function getExcludedFromEqualsFor(constructor: Function): string[] { export function getExcludedFromEqualsFor(constructor: Function): string[] {
return excludedFromEquals.get(constructor) || []; return excludedFromEquals.get(constructor) || [];
} }
@@ -113,7 +113,7 @@ export function fieldsForEquals(...fields: string[]): any {
}; };
} }
// tslint:disable-next-line:ban-types // eslint-disable-next-line @typescript-eslint/ban-types
export function getFieldsForEquals(constructor: Function, field: string): string[] { export function getFieldsForEquals(constructor: Function, field: string): string[] {
const fieldMap = fieldsForEqualsMap.get(constructor) || new Map(); const fieldMap = fieldsForEqualsMap.get(constructor) || new Map();
return fieldMap.get(field); return fieldMap.get(field);

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import { EquatableObject, excludeFromEquals, fieldsForEquals } from './equals.decorators'; import { EquatableObject, excludeFromEquals, fieldsForEquals } from './equals.decorators';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
@@ -13,7 +14,6 @@ class Dog extends EquatableObject<Dog> {
public favouriteToy: { name: string, colour: string }; public favouriteToy: { name: string, colour: string };
} }
// tslint:disable-next-line:max-classes-per-file
class Owner extends EquatableObject<Owner> { class Owner extends EquatableObject<Owner> {
@excludeFromEquals @excludeFromEquals
favouriteFood: string; favouriteFood: string;

View File

@@ -2,7 +2,7 @@
<h2 class="item-page-title-field mr-auto"> <h2 class="item-page-title-field mr-auto">
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> {{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2 space-children-mr">
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More