mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge remote-tracking branch 'upstream/main' into minor-themed-component-fixes_contribute-main
# Conflicts: # src/app/admin/admin-routes.ts
This commit is contained in:
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -33,12 +33,12 @@ jobs:
|
|||||||
#CHROME_VERSION: "90.0.4430.212-1"
|
#CHROME_VERSION: "90.0.4430.212-1"
|
||||||
# Bump Node heap size (OOM in CI after upgrading to Angular 15)
|
# Bump Node heap size (OOM in CI after upgrading to Angular 15)
|
||||||
NODE_OPTIONS: '--max-old-space-size=4096'
|
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||||
# Project name to use when running docker-compose prior to e2e tests
|
# Project name to use when running "docker compose" prior to e2e tests
|
||||||
COMPOSE_PROJECT_NAME: 'ci'
|
COMPOSE_PROJECT_NAME: 'ci'
|
||||||
strategy:
|
strategy:
|
||||||
# Create a matrix of Node versions to test against (in parallel)
|
# Create a matrix of Node versions to test against (in parallel)
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16.x, 18.x]
|
node-version: [18.x, 20.x]
|
||||||
# Do NOT exit immediately if one matrix job fails
|
# Do NOT exit immediately if one matrix job fails
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
# These are the actual CI steps to perform per job
|
# These are the actual CI steps to perform per job
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
- name: Cache Yarn dependencies
|
- name: Cache Yarn dependencies
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
# Cache entire Yarn cache directory (see previous step)
|
# Cache entire Yarn cache directory (see previous step)
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
@@ -101,19 +101,19 @@ jobs:
|
|||||||
# so that it can be shared with the 'codecov' job (see below)
|
# so that it can be shared with the 'codecov' job (see below)
|
||||||
# NOTE: Angular CLI only supports code coverage for specs. See https://github.com/angular/angular-cli/issues/6286
|
# NOTE: Angular CLI only supports code coverage for specs. See https://github.com/angular/angular-cli/issues/6286
|
||||||
- name: Upload code coverage report to Artifact
|
- name: Upload code coverage report to Artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: matrix.node-version == '18.x'
|
if: matrix.node-version == '18.x'
|
||||||
with:
|
with:
|
||||||
name: dspace-angular coverage report
|
name: coverage-report-${{ matrix.node-version }}
|
||||||
path: 'coverage/dspace-angular/lcov.info'
|
path: 'coverage/dspace-angular/lcov.info'
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|
||||||
# Using docker-compose start backend using CI configuration
|
# Using "docker compose" start backend using CI configuration
|
||||||
# and load assetstore from a cached copy
|
# and load assetstore from a cached copy
|
||||||
- name: Start DSpace REST Backend via Docker (for e2e tests)
|
- name: Start DSpace REST Backend via Docker (for e2e tests)
|
||||||
run: |
|
run: |
|
||||||
docker-compose -f ./docker/docker-compose-ci.yml up -d
|
docker compose -f ./docker/docker-compose-ci.yml up -d
|
||||||
docker-compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli
|
docker compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli
|
||||||
docker container ls
|
docker container ls
|
||||||
|
|
||||||
# Run integration tests via Cypress.io
|
# Run integration tests via Cypress.io
|
||||||
@@ -135,19 +135,19 @@ jobs:
|
|||||||
# Cypress always creates a video of all e2e tests (whether they succeeded or failed)
|
# Cypress always creates a video of all e2e tests (whether they succeeded or failed)
|
||||||
# Save those in an Artifact
|
# Save those in an Artifact
|
||||||
- name: Upload e2e test videos to Artifacts
|
- name: Upload e2e test videos to Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: e2e-test-videos
|
name: e2e-test-videos-${{ matrix.node-version }}
|
||||||
path: cypress/videos
|
path: cypress/videos
|
||||||
|
|
||||||
# If e2e tests fail, Cypress creates a screenshot of what happened
|
# If e2e tests fail, Cypress creates a screenshot of what happened
|
||||||
# Save those in an Artifact
|
# Save those in an Artifact
|
||||||
- name: Upload e2e test failure screenshots to Artifacts
|
- name: Upload e2e test failure screenshots to Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: e2e-test-screenshots
|
name: e2e-test-screenshots-${{ matrix.node-version }}
|
||||||
path: cypress/screenshots
|
path: cypress/screenshots
|
||||||
|
|
||||||
- name: Stop app (in case it stays up after e2e tests)
|
- name: Stop app (in case it stays up after e2e tests)
|
||||||
@@ -182,7 +182,7 @@ jobs:
|
|||||||
run: kill -9 $(lsof -t -i:4000)
|
run: kill -9 $(lsof -t -i:4000)
|
||||||
|
|
||||||
- name: Shutdown Docker containers
|
- name: Shutdown Docker containers
|
||||||
run: docker-compose -f ./docker/docker-compose-ci.yml down
|
run: docker compose -f ./docker/docker-compose-ci.yml down
|
||||||
|
|
||||||
# Codecov upload is a separate job in order to allow us to restart this separate from the entire build/test
|
# Codecov upload is a separate job in order to allow us to restart this separate from the entire build/test
|
||||||
# job above. This is necessary because Codecov uploads seem to randomly fail at times.
|
# job above. This is necessary because Codecov uploads seem to randomly fail at times.
|
||||||
@@ -197,7 +197,7 @@ jobs:
|
|||||||
|
|
||||||
# Download artifacts from previous 'tests' job
|
# Download artifacts from previous 'tests' job
|
||||||
- name: Download coverage artifacts
|
- name: Download coverage artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
# Now attempt upload to Codecov using its action.
|
# Now attempt upload to Codecov using its action.
|
||||||
# NOTE: We use a retry action to retry the Codecov upload if it fails the first time.
|
# NOTE: We use a retry action to retry the Codecov upload if it fails the first time.
|
||||||
@@ -207,11 +207,12 @@ jobs:
|
|||||||
- name: Upload coverage to Codecov.io
|
- name: Upload coverage to Codecov.io
|
||||||
uses: Wandalen/wretry.action@v1.3.0
|
uses: Wandalen/wretry.action@v1.3.0
|
||||||
with:
|
with:
|
||||||
action: codecov/codecov-action@v3
|
action: codecov/codecov-action@v4
|
||||||
# Ensure codecov-action throws an error when it fails to upload
|
# Ensure codecov-action throws an error when it fails to upload
|
||||||
# This allows us to auto-restart the action if an error is thrown
|
# This allows us to auto-restart the action if an error is thrown
|
||||||
with: |
|
with: |
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
# Try re-running action 5 times max
|
# Try re-running action 5 times max
|
||||||
attempt_limit: 5
|
attempt_limit: 5
|
||||||
# Run again in 30 seconds
|
# Run again in 30 seconds
|
||||||
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
# Use the reusable-docker-build.yml script from DSpace/DSpace repo to build our Docker image
|
# Use the reusable-docker-build.yml script from DSpace/DSpace repo to build our Docker image
|
||||||
uses: DSpace/DSpace/.github/workflows/reusable-docker-build.yml@main
|
uses: DSpace/DSpace/.github/workflows/reusable-docker-build.yml@main
|
||||||
with:
|
with:
|
||||||
build_id: dspace-angular
|
build_id: dspace-angular-dev
|
||||||
image_name: dspace/dspace-angular
|
image_name: dspace/dspace-angular
|
||||||
dockerfile_path: ./Dockerfile
|
dockerfile_path: ./Dockerfile
|
||||||
secrets:
|
secrets:
|
||||||
|
2
.github/workflows/issue_opened.yml
vendored
2
.github/workflows/issue_opened.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
# Only add to project board if issue is flagged as "needs triage" or has no labels
|
# Only add to project board if issue is flagged as "needs triage" or has no labels
|
||||||
# NOTE: By default we flag new issues as "needs triage" in our issue template
|
# NOTE: By default we flag new issues as "needs triage" in our issue template
|
||||||
if: (contains(github.event.issue.labels.*.name, 'needs triage') || join(github.event.issue.labels.*.name) == '')
|
if: (contains(github.event.issue.labels.*.name, 'needs triage') || join(github.event.issue.labels.*.name) == '')
|
||||||
uses: actions/add-to-project@v0.5.0
|
uses: actions/add-to-project@v1.0.0
|
||||||
# Note, the authentication token below is an ORG level Secret.
|
# Note, the authentication token below is an ORG level Secret.
|
||||||
# It must be created/recreated manually via a personal access token with admin:org, project, public_repo permissions
|
# It must be created/recreated manually via a personal access token with admin:org, project, public_repo permissions
|
||||||
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token
|
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token
|
||||||
|
2
.github/workflows/pull_request_opened.yml
vendored
2
.github/workflows/pull_request_opened.yml
vendored
@@ -21,4 +21,4 @@ jobs:
|
|||||||
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
|
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
|
||||||
# See https://github.com/toshimaru/auto-author-assign
|
# See https://github.com/toshimaru/auto-author-assign
|
||||||
- name: Assign PR to creator
|
- name: Assign PR to creator
|
||||||
uses: toshimaru/auto-author-assign@v2.0.1
|
uses: toshimaru/auto-author-assign@v2.1.0
|
||||||
|
@@ -17,6 +17,13 @@ ui:
|
|||||||
# Trust X-FORWARDED-* headers from proxies (default = true)
|
# Trust X-FORWARDED-* headers from proxies (default = true)
|
||||||
useProxies: true
|
useProxies: true
|
||||||
|
|
||||||
|
universal:
|
||||||
|
# Whether to inline "critical" styles into the server-side rendered HTML.
|
||||||
|
# Determining which styles are critical is a relatively expensive operation;
|
||||||
|
# this option can be disabled to boost server performance at the expense of
|
||||||
|
# loading smoothness.
|
||||||
|
inlineCriticalCss: true
|
||||||
|
|
||||||
# The REST API server settings
|
# The REST API server settings
|
||||||
# NOTE: these settings define which (publicly available) REST API to use. They are usually
|
# NOTE: these settings define which (publicly available) REST API to use. They are usually
|
||||||
# 'synced' with the 'dspace.server.url' setting in your backend's local.cfg.
|
# 'synced' with the 'dspace.server.url' setting in your backend's local.cfg.
|
||||||
@@ -400,10 +407,11 @@ mediaViewer:
|
|||||||
|
|
||||||
# Whether the end user agreement is required before users use the repository.
|
# Whether the end user agreement is required before users use the repository.
|
||||||
# If enabled, the user will be required to accept the agreement before they can use the repository.
|
# If enabled, the user will be required to accept the agreement before they can use the repository.
|
||||||
# And whether the privacy statement should exist or not.
|
# And whether the privacy statement/COAR notify support page should exist or not.
|
||||||
info:
|
info:
|
||||||
enableEndUserAgreement: true
|
enableEndUserAgreement: true
|
||||||
enablePrivacyStatement: true
|
enablePrivacyStatement: true
|
||||||
|
enableCOARNotifySupport: true
|
||||||
|
|
||||||
# Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)
|
# Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)
|
||||||
# display in supported metadata fields. By default, only dc.description.abstract is supported.
|
# display in supported metadata fields. By default, only dc.description.abstract is supported.
|
||||||
|
74
package.json
74
package.json
@@ -55,28 +55,28 @@
|
|||||||
"ts-node": "10.2.1"
|
"ts-node": "10.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^15.2.8",
|
"@angular/animations": "^16.2.12",
|
||||||
"@angular/cdk": "^15.2.8",
|
"@angular/cdk": "^16.2.12",
|
||||||
"@angular/common": "^15.2.8",
|
"@angular/common": "^16.2.12",
|
||||||
"@angular/compiler": "^15.2.8",
|
"@angular/compiler": "^16.2.12",
|
||||||
"@angular/core": "^15.2.8",
|
"@angular/core": "^16.2.12",
|
||||||
"@angular/forms": "^15.2.8",
|
"@angular/forms": "^16.2.12",
|
||||||
"@angular/localize": "15.2.8",
|
"@angular/localize": "16.2.12",
|
||||||
"@angular/platform-browser": "^15.2.8",
|
"@angular/platform-browser": "^16.2.12",
|
||||||
"@angular/platform-browser-dynamic": "^15.2.8",
|
"@angular/platform-browser-dynamic": "^16.2.12",
|
||||||
"@angular/platform-server": "^15.2.8",
|
"@angular/platform-server": "^16.2.12",
|
||||||
"@angular/router": "^15.2.8",
|
"@angular/router": "^16.2.12",
|
||||||
"@babel/runtime": "7.21.0",
|
"@babel/runtime": "7.21.0",
|
||||||
"@kolkov/ngx-gallery": "^2.0.1",
|
"@kolkov/ngx-gallery": "^2.0.1",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@material-ui/icons": "^4.11.3",
|
"@material-ui/icons": "^4.11.3",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
|
||||||
"@ng-dynamic-forms/core": "^15.0.0",
|
"@ng-dynamic-forms/core": "^16.0.0",
|
||||||
"@ng-dynamic-forms/ui-ng-bootstrap": "^15.0.0",
|
"@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0",
|
||||||
"@ngrx/effects": "^15.4.0",
|
"@ngrx/effects": "^16.3.0",
|
||||||
"@ngrx/router-store": "^15.4.0",
|
"@ngrx/router-store": "^16.3.0",
|
||||||
"@ngrx/store": "^15.4.0",
|
"@ngrx/store": "^16.3.0",
|
||||||
"@nguniversal/express-engine": "^15.2.1",
|
"@nguniversal/express-engine": "^16.2.0",
|
||||||
"@ngx-translate/core": "^14.0.0",
|
"@ngx-translate/core": "^14.0.0",
|
||||||
"@nicky-lenaers/ngx-scroll-to": "^14.0.0",
|
"@nicky-lenaers/ngx-scroll-to": "^14.0.0",
|
||||||
"@types/grecaptcha": "^3.0.4",
|
"@types/grecaptcha": "^3.0.4",
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
"date-fns-tz": "^1.3.7",
|
"date-fns-tz": "^1.3.7",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"ejs": "^3.1.9",
|
"ejs": "^3.1.9",
|
||||||
"express": "^4.18.2",
|
"express": "^4.19.2",
|
||||||
"express-rate-limit": "^5.1.3",
|
"express-rate-limit": "^5.1.3",
|
||||||
"fast-json-patch": "^3.1.1",
|
"fast-json-patch": "^3.1.1",
|
||||||
"filesize": "^6.1.0",
|
"filesize": "^6.1.0",
|
||||||
@@ -110,17 +110,15 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lru-cache": "^7.14.1",
|
"lru-cache": "^7.14.1",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"markdown-it-mathjax3": "^4.3.2",
|
|
||||||
"mirador": "^3.3.0",
|
"mirador": "^3.3.0",
|
||||||
"mirador-dl-plugin": "^0.13.0",
|
"mirador-dl-plugin": "^0.13.0",
|
||||||
"mirador-share-plugin": "^0.11.0",
|
"mirador-share-plugin": "^0.11.0",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ng-mocks": "^14.10.0",
|
"ng-mocks": "^14.10.0",
|
||||||
"ng2-file-upload": "1.4.0",
|
"ng2-file-upload": "5.0.0",
|
||||||
"ng2-nouislider": "^2.0.0",
|
"ng2-nouislider": "^2.0.0",
|
||||||
"ngx-infinite-scroll": "^15.0.0",
|
"ngx-infinite-scroll": "^16.0.0",
|
||||||
"ngx-pagination": "6.0.3",
|
"ngx-pagination": "6.0.3",
|
||||||
"ngx-sortablejs": "^11.1.0",
|
|
||||||
"ngx-ui-switch": "^14.1.0",
|
"ngx-ui-switch": "^14.1.0",
|
||||||
"nouislider": "^15.7.1",
|
"nouislider": "^15.7.1",
|
||||||
"pem": "1.14.7",
|
"pem": "1.14.7",
|
||||||
@@ -132,24 +130,24 @@
|
|||||||
"sortablejs": "1.15.0",
|
"sortablejs": "1.15.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"webfontloader": "1.6.28",
|
"webfontloader": "1.6.28",
|
||||||
"zone.js": "~0.11.5"
|
"zone.js": "~0.13.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "~15.0.0",
|
"@angular-builders/custom-webpack": "~16.0.0",
|
||||||
"@angular-devkit/build-angular": "^15.2.6",
|
"@angular-devkit/build-angular": "^16.2.12",
|
||||||
"@angular-eslint/builder": "15.2.1",
|
"@angular-eslint/builder": "16.3.1",
|
||||||
"@angular-eslint/eslint-plugin": "15.2.1",
|
"@angular-eslint/eslint-plugin": "16.3.1",
|
||||||
"@angular-eslint/eslint-plugin-template": "15.2.1",
|
"@angular-eslint/eslint-plugin-template": "16.3.1",
|
||||||
"@angular-eslint/schematics": "15.2.1",
|
"@angular-eslint/schematics": "16.3.1",
|
||||||
"@angular-eslint/template-parser": "15.2.1",
|
"@angular-eslint/template-parser": "16.3.1",
|
||||||
"@angular/cli": "^16.0.4",
|
"@angular/cli": "^16.2.12",
|
||||||
"@angular/compiler-cli": "^15.2.8",
|
"@angular/compiler-cli": "^16.2.12",
|
||||||
"@angular/language-service": "^15.2.8",
|
"@angular/language-service": "^16.2.12",
|
||||||
"@cypress/schematic": "^1.5.0",
|
"@cypress/schematic": "^1.5.0",
|
||||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||||
"@ngrx/store-devtools": "^15.4.0",
|
"@ngrx/store-devtools": "^16.3.0",
|
||||||
"@ngtools/webpack": "^15.2.6",
|
"@ngtools/webpack": "^16.2.12",
|
||||||
"@nguniversal/builders": "^15.2.1",
|
"@nguniversal/builders": "^16.2.0",
|
||||||
"@types/deep-freeze": "0.1.2",
|
"@types/deep-freeze": "0.1.2",
|
||||||
"@types/ejs": "^3.1.2",
|
"@types/ejs": "^3.1.2",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
@@ -186,7 +184,7 @@
|
|||||||
"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": "^13.1.7",
|
"ngx-mask": "14.2.4",
|
||||||
"nodemon": "^2.0.22",
|
"nodemon": "^2.0.22",
|
||||||
"postcss": "^8.4",
|
"postcss": "^8.4",
|
||||||
"postcss-apply": "0.12.0",
|
"postcss-apply": "0.12.0",
|
||||||
@@ -202,7 +200,7 @@
|
|||||||
"sass-loader": "^12.6.0",
|
"sass-loader": "^12.6.0",
|
||||||
"sass-resources-loader": "^2.2.5",
|
"sass-resources-loader": "^2.2.5",
|
||||||
"ts-node": "^8.10.2",
|
"ts-node": "^8.10.2",
|
||||||
"typescript": "~4.8.4",
|
"typescript": "~4.9.3",
|
||||||
"webpack": "5.76.1",
|
"webpack": "5.76.1",
|
||||||
"webpack-bundle-analyzer": "^4.8.0",
|
"webpack-bundle-analyzer": "^4.8.0",
|
||||||
"webpack-cli": "^4.2.0",
|
"webpack-cli": "^4.2.0",
|
||||||
|
39
server.ts
39
server.ts
@@ -48,7 +48,7 @@ import { hasNoValue, hasValue } from './src/app/shared/empty.util';
|
|||||||
|
|
||||||
import { UIServerConfig } from './src/config/ui-server-config.interface';
|
import { UIServerConfig } from './src/config/ui-server-config.interface';
|
||||||
|
|
||||||
import { ServerAppModule } from './src/main.server';
|
import bootstrap from './src/main.server';
|
||||||
|
|
||||||
import { buildAppConfig } from './src/config/config.server';
|
import { buildAppConfig } from './src/config/config.server';
|
||||||
import { APP_CONFIG, AppConfig } from './src/config/app-config.interface';
|
import { APP_CONFIG, AppConfig } from './src/config/app-config.interface';
|
||||||
@@ -130,7 +130,8 @@ export function app() {
|
|||||||
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
|
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
|
||||||
server.engine('html', (_, options, callback) =>
|
server.engine('html', (_, options, callback) =>
|
||||||
ngExpressEngine({
|
ngExpressEngine({
|
||||||
bootstrap: ServerAppModule,
|
bootstrap,
|
||||||
|
inlineCriticalCss: environment.universal.inlineCriticalCss,
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: REQUEST,
|
provide: REQUEST,
|
||||||
@@ -142,10 +143,10 @@ export function app() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: APP_CONFIG,
|
provide: APP_CONFIG,
|
||||||
useValue: environment
|
useValue: environment,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})(_, (options as any), callback)
|
})(_, (options as any), callback),
|
||||||
);
|
);
|
||||||
|
|
||||||
server.engine('ejs', ejs.renderFile);
|
server.engine('ejs', ejs.renderFile);
|
||||||
@@ -162,7 +163,7 @@ export function app() {
|
|||||||
server.get('/robots.txt', (req, res) => {
|
server.get('/robots.txt', (req, res) => {
|
||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
res.render('assets/robots.txt.ejs', {
|
res.render('assets/robots.txt.ejs', {
|
||||||
'origin': req.protocol + '://' + req.headers.host
|
'origin': req.protocol + '://' + req.headers.host,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ export function app() {
|
|||||||
router.use('/sitemap**', createProxyMiddleware({
|
router.use('/sitemap**', createProxyMiddleware({
|
||||||
target: `${environment.rest.baseUrl}/sitemaps`,
|
target: `${environment.rest.baseUrl}/sitemaps`,
|
||||||
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
||||||
changeOrigin: true
|
changeOrigin: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,7 +187,7 @@ export function app() {
|
|||||||
router.use('/signposting**', createProxyMiddleware({
|
router.use('/signposting**', createProxyMiddleware({
|
||||||
target: `${environment.rest.baseUrl}`,
|
target: `${environment.rest.baseUrl}`,
|
||||||
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
||||||
changeOrigin: true
|
changeOrigin: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -197,7 +198,7 @@ export function app() {
|
|||||||
const RateLimit = require('express-rate-limit');
|
const RateLimit = require('express-rate-limit');
|
||||||
const limiter = new RateLimit({
|
const limiter = new RateLimit({
|
||||||
windowMs: (environment.ui as UIServerConfig).rateLimiter.windowMs,
|
windowMs: (environment.ui as UIServerConfig).rateLimiter.windowMs,
|
||||||
max: (environment.ui as UIServerConfig).rateLimiter.max
|
max: (environment.ui as UIServerConfig).rateLimiter.max,
|
||||||
});
|
});
|
||||||
server.use(limiter);
|
server.use(limiter);
|
||||||
}
|
}
|
||||||
@@ -325,7 +326,7 @@ function initCache() {
|
|||||||
botCache = new LRU( {
|
botCache = new LRU( {
|
||||||
max: environment.cache.serverSide.botCache.max,
|
max: environment.cache.serverSide.botCache.max,
|
||||||
ttl: environment.cache.serverSide.botCache.timeToLive,
|
ttl: environment.cache.serverSide.botCache.timeToLive,
|
||||||
allowStale: environment.cache.serverSide.botCache.allowStale
|
allowStale: environment.cache.serverSide.botCache.allowStale,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +338,7 @@ function initCache() {
|
|||||||
anonymousCache = new LRU( {
|
anonymousCache = new LRU( {
|
||||||
max: environment.cache.serverSide.anonymousCache.max,
|
max: environment.cache.serverSide.anonymousCache.max,
|
||||||
ttl: environment.cache.serverSide.anonymousCache.timeToLive,
|
ttl: environment.cache.serverSide.anonymousCache.timeToLive,
|
||||||
allowStale: environment.cache.serverSide.anonymousCache.allowStale
|
allowStale: environment.cache.serverSide.anonymousCache.allowStale,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,7 +416,7 @@ function checkCacheForRequest(cacheName: string, cache: LRU<string, any>, req, r
|
|||||||
const key = getCacheKey(req);
|
const key = getCacheKey(req);
|
||||||
|
|
||||||
// Check if this page is in our cache
|
// Check if this page is in our cache
|
||||||
let cachedCopy = cache.get(key);
|
const cachedCopy = cache.get(key);
|
||||||
if (cachedCopy) {
|
if (cachedCopy) {
|
||||||
if (environment.cache.serverSide.debug) { console.log(`CACHE HIT FOR ${key} in ${cacheName} cache`); }
|
if (environment.cache.serverSide.debug) { console.log(`CACHE HIT FOR ${key} in ${cacheName} cache`); }
|
||||||
|
|
||||||
@@ -529,13 +530,13 @@ function serverStarted() {
|
|||||||
function createHttpsServer(keys) {
|
function createHttpsServer(keys) {
|
||||||
const listener = createServer({
|
const listener = createServer({
|
||||||
key: keys.serviceKey,
|
key: keys.serviceKey,
|
||||||
cert: keys.certificate
|
cert: keys.certificate,
|
||||||
}, app).listen(environment.ui.port, environment.ui.host, () => {
|
}, app).listen(environment.ui.port, environment.ui.host, () => {
|
||||||
serverStarted();
|
serverStarted();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Graceful shutdown when signalled
|
// Graceful shutdown when signalled
|
||||||
const terminator = createHttpTerminator({server: listener});
|
const terminator = createHttpTerminator({ server: listener });
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
void (async ()=> {
|
void (async ()=> {
|
||||||
console.debug('Closing HTTPS server on signal');
|
console.debug('Closing HTTPS server on signal');
|
||||||
@@ -559,7 +560,7 @@ function run() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Graceful shutdown when signalled
|
// Graceful shutdown when signalled
|
||||||
const terminator = createHttpTerminator({server: listener});
|
const terminator = createHttpTerminator({ server: listener });
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
void (async () => {
|
void (async () => {
|
||||||
console.debug('Closing HTTP server on signal');
|
console.debug('Closing HTTP server on signal');
|
||||||
@@ -597,7 +598,7 @@ function start() {
|
|||||||
if (serviceKey && certificate) {
|
if (serviceKey && certificate) {
|
||||||
createHttpsServer({
|
createHttpsServer({
|
||||||
serviceKey: serviceKey,
|
serviceKey: serviceKey,
|
||||||
certificate: certificate
|
certificate: certificate,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn('Disabling certificate validation and proceeding with a self-signed certificate. If this is a production server, it is recommended that you configure a valid certificate instead.');
|
console.warn('Disabling certificate validation and proceeding with a self-signed certificate. If this is a production server, it is recommended that you configure a valid certificate instead.');
|
||||||
@@ -606,7 +607,7 @@ function start() {
|
|||||||
|
|
||||||
createCertificate({
|
createCertificate({
|
||||||
days: 1,
|
days: 1,
|
||||||
selfSigned: true
|
selfSigned: true,
|
||||||
}, (error, keys) => {
|
}, (error, keys) => {
|
||||||
createHttpsServer(keys);
|
createHttpsServer(keys);
|
||||||
});
|
});
|
||||||
@@ -627,7 +628,7 @@ function healthCheck(req, res) {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
res.status(error.response.status).send({
|
res.status(error.response.status).send({
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
import { AbstractControl } from '@angular/forms';
|
import { AbstractControl } from '@angular/forms';
|
||||||
import { Route } from '@angular/router';
|
import {
|
||||||
|
mapToCanActivate,
|
||||||
|
Route,
|
||||||
|
} from '@angular/router';
|
||||||
import {
|
import {
|
||||||
DYNAMIC_ERROR_MESSAGES_MATCHER,
|
DYNAMIC_ERROR_MESSAGES_MATCHER,
|
||||||
DynamicErrorMessagesMatcher,
|
DynamicErrorMessagesMatcher,
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { GroupAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
|
import { GroupAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
|
||||||
import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
||||||
import {
|
import {
|
||||||
@@ -39,76 +42,76 @@ export const ROUTES: Route[] = [
|
|||||||
path: EPERSON_PATH,
|
path: EPERSON_PATH,
|
||||||
component: EPeopleRegistryComponent,
|
component: EPeopleRegistryComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
providers,
|
providers,
|
||||||
data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' },
|
data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' },
|
||||||
canActivate: [SiteAdministratorGuard],
|
canActivate: mapToCanActivate([SiteAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${EPERSON_PATH}/create`,
|
path: `${EPERSON_PATH}/create`,
|
||||||
component: EPersonFormComponent,
|
component: EPersonFormComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
providers,
|
providers,
|
||||||
data: { title: 'admin.access-control.epeople.add.title', breadcrumbKey: 'admin.access-control.epeople.add' },
|
data: { title: 'admin.access-control.epeople.add.title', breadcrumbKey: 'admin.access-control.epeople.add' },
|
||||||
canActivate: [SiteAdministratorGuard],
|
canActivate: mapToCanActivate([SiteAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${EPERSON_PATH}/:id/edit`,
|
path: `${EPERSON_PATH}/:id/edit`,
|
||||||
component: EPersonFormComponent,
|
component: EPersonFormComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
ePerson: EPersonResolver,
|
ePerson: EPersonResolver,
|
||||||
},
|
},
|
||||||
providers,
|
providers,
|
||||||
data: { title: 'admin.access-control.epeople.edit.title', breadcrumbKey: 'admin.access-control.epeople.edit' },
|
data: { title: 'admin.access-control.epeople.edit.title', breadcrumbKey: 'admin.access-control.epeople.edit' },
|
||||||
canActivate: [SiteAdministratorGuard],
|
canActivate: mapToCanActivate([SiteAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: GROUP_PATH,
|
path: GROUP_PATH,
|
||||||
component: GroupsRegistryComponent,
|
component: GroupsRegistryComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
providers,
|
providers,
|
||||||
data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' },
|
data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' },
|
||||||
canActivate: [GroupAdministratorGuard],
|
canActivate: mapToCanActivate([GroupAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${GROUP_PATH}/create`,
|
path: `${GROUP_PATH}/create`,
|
||||||
component: GroupFormComponent,
|
component: GroupFormComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
providers,
|
providers,
|
||||||
data: {
|
data: {
|
||||||
title: 'admin.access-control.groups.title.addGroup',
|
title: 'admin.access-control.groups.title.addGroup',
|
||||||
breadcrumbKey: 'admin.access-control.groups.addGroup',
|
breadcrumbKey: 'admin.access-control.groups.addGroup',
|
||||||
},
|
},
|
||||||
canActivate: [GroupAdministratorGuard],
|
canActivate: mapToCanActivate([GroupAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${GROUP_PATH}/:groupId/edit`,
|
path: `${GROUP_PATH}/:groupId/edit`,
|
||||||
component: GroupFormComponent,
|
component: GroupFormComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
providers,
|
providers,
|
||||||
data: {
|
data: {
|
||||||
title: 'admin.access-control.groups.title.singleGroup',
|
title: 'admin.access-control.groups.title.singleGroup',
|
||||||
breadcrumbKey: 'admin.access-control.groups.singleGroup',
|
breadcrumbKey: 'admin.access-control.groups.singleGroup',
|
||||||
},
|
},
|
||||||
canActivate: [GroupPageGuard],
|
canActivate: mapToCanActivate([GroupPageGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'bulk-access',
|
path: 'bulk-access',
|
||||||
component: BulkAccessComponent,
|
component: BulkAccessComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' },
|
data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' },
|
||||||
canActivate: [SiteAdministratorGuard],
|
canActivate: mapToCanActivate([SiteAdministratorGuard]),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@@ -27,7 +26,7 @@ export const EPERSON_EDIT_FOLLOW_LINKS: FollowLinkConfig<EPerson>[] = [
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class EPersonResolver implements Resolve<RemoteData<EPerson>> {
|
export class EPersonResolver {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected ePersonService: EPersonDataService,
|
protected ePersonService: EPersonDataService,
|
||||||
|
@@ -53,6 +53,7 @@ import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
|||||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { UUIDService } from '../../../core/shared/uuid.service';
|
import { UUIDService } from '../../../core/shared/uuid.service';
|
||||||
|
import { XSRFService } from '../../../core/xsrf/xsrf.service';
|
||||||
import { AlertComponent } from '../../../shared/alert/alert.component';
|
import { AlertComponent } from '../../../shared/alert/alert.component';
|
||||||
import { ContextHelpDirective } from '../../../shared/context-help.directive';
|
import { ContextHelpDirective } from '../../../shared/context-help.directive';
|
||||||
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
|
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
|
||||||
@@ -244,6 +245,7 @@ describe('GroupFormComponent', () => {
|
|||||||
{ provide: HttpClient, useValue: {} },
|
{ provide: HttpClient, useValue: {} },
|
||||||
{ provide: ObjectCacheService, useValue: {} },
|
{ provide: ObjectCacheService, useValue: {} },
|
||||||
{ provide: UUIDService, useValue: {} },
|
{ provide: UUIDService, useValue: {} },
|
||||||
|
{ provide: XSRFService, useValue: {} },
|
||||||
{ provide: Store, useValue: {} },
|
{ provide: Store, useValue: {} },
|
||||||
{ provide: RemoteDataBuildService, useValue: {} },
|
{ provide: RemoteDataBuildService, useValue: {} },
|
||||||
{ provide: HALEndpointService, useValue: {} },
|
{ provide: HALEndpointService, useValue: {} },
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { NavigationBreadcrumbResolver } from '../../core/breadcrumbs/navigation-breadcrumb.resolver';
|
import { navigationBreadcrumbResolver } from '../../core/breadcrumbs/navigation-breadcrumb.resolver';
|
||||||
import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component';
|
import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component';
|
||||||
import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component';
|
import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component';
|
||||||
|
|
||||||
@@ -10,18 +10,18 @@ const moduleRoutes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: LdnServicesOverviewComponent,
|
component: LdnServicesOverviewComponent,
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
data: { title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new' },
|
data: { title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'new',
|
path: 'new',
|
||||||
resolve: { breadcrumb: NavigationBreadcrumbResolver },
|
resolve: { breadcrumb: navigationBreadcrumbResolver },
|
||||||
component: LdnServiceFormComponent,
|
component: LdnServiceFormComponent,
|
||||||
data: { title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service' },
|
data: { title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'edit/:serviceId',
|
path: 'edit/:serviceId',
|
||||||
resolve: { breadcrumb: NavigationBreadcrumbResolver },
|
resolve: { breadcrumb: navigationBreadcrumbResolver },
|
||||||
component: LdnServiceFormComponent,
|
component: LdnServiceFormComponent,
|
||||||
data: { title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service' },
|
data: { title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service' },
|
||||||
},
|
},
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ export interface NotificationsSuggestionTargetsPageParams {
|
|||||||
* This class represents a resolver that retrieve the route data before the route is activated.
|
* This class represents a resolver that retrieve the route data before the route is activated.
|
||||||
*/
|
*/
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class NotificationsSuggestionTargetsPageResolver implements Resolve<NotificationsSuggestionTargetsPageParams> {
|
export class NotificationsSuggestionTargetsPageResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for resolving the parameters in the current route.
|
* Method for resolving the parameters in the current route.
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { AuthenticatedGuard } from '../../core/auth/authenticated.guard';
|
import { authenticatedGuard } from '../../core/auth/authenticated.guard';
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { QualityAssuranceBreadcrumbResolver } from '../../core/breadcrumbs/quality-assurance-breadcrumb.resolver';
|
import { qualityAssuranceBreadcrumbResolver } from '../../core/breadcrumbs/quality-assurance-breadcrumb.resolver';
|
||||||
import { AdminNotificationsPublicationClaimPageResolver } from '../../quality-assurance-notifications-pages/notifications-suggestion-targets-page/notifications-suggestion-targets-page-resolver.service';
|
import { AdminNotificationsPublicationClaimPageResolver } from '../../quality-assurance-notifications-pages/notifications-suggestion-targets-page/notifications-suggestion-targets-page-resolver.service';
|
||||||
import { QualityAssuranceEventsPageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component';
|
import { QualityAssuranceEventsPageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component';
|
||||||
import { QualityAssuranceEventsPageResolver } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.resolver';
|
import { qualityAssuranceEventsPageResolver } from '../../quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.resolver';
|
||||||
import { SourceDataResolver } from '../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-data.resolver';
|
import { qualityAssuranceSourceDataResolver } from '../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-data.resolver';
|
||||||
import { QualityAssuranceSourcePageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page.component';
|
import { QualityAssuranceSourcePageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page.component';
|
||||||
import { QualityAssuranceSourcePageResolver } from '../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page-resolver.service';
|
import { QualityAssuranceSourcePageResolver } from '../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page-resolver.service';
|
||||||
import { QualityAssuranceTopicsPageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page.component';
|
import { QualityAssuranceTopicsPageComponent } from '../../quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page.component';
|
||||||
@@ -19,12 +19,12 @@ import {
|
|||||||
|
|
||||||
export const ROUTES: Route[] = [
|
export const ROUTES: Route[] = [
|
||||||
{
|
{
|
||||||
canActivate: [ AuthenticatedGuard ],
|
canActivate: [ authenticatedGuard ],
|
||||||
path: `${PUBLICATION_CLAIMS_PATH}`,
|
path: `${PUBLICATION_CLAIMS_PATH}`,
|
||||||
component: AdminNotificationsPublicationClaimPageComponent,
|
component: AdminNotificationsPublicationClaimPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
suggestionTargetParams: AdminNotificationsPublicationClaimPageResolver,
|
suggestionTargetParams: AdminNotificationsPublicationClaimPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -34,12 +34,12 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
||||||
component: QualityAssuranceTopicsPageComponent,
|
component: QualityAssuranceTopicsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: QualityAssuranceBreadcrumbResolver,
|
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
||||||
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -49,12 +49,12 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
canActivate: [ AuthenticatedGuard ],
|
canActivate: [ authenticatedGuard ],
|
||||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/target/:targetId`,
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/target/:targetId`,
|
||||||
component: QualityAssuranceTopicsPageComponent,
|
component: QualityAssuranceTopicsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
openaireQualityAssuranceTopicsParams: QualityAssuranceTopicsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -64,14 +64,14 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}`,
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}`,
|
||||||
component: QualityAssuranceSourcePageComponent,
|
component: QualityAssuranceSourcePageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
openaireQualityAssuranceSourceParams: QualityAssuranceSourcePageResolver,
|
openaireQualityAssuranceSourceParams: QualityAssuranceSourcePageResolver,
|
||||||
sourceData: SourceDataResolver,
|
sourceData: qualityAssuranceSourceDataResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
title: 'admin.notifications.source.breadcrumbs',
|
title: 'admin.notifications.source.breadcrumbs',
|
||||||
@@ -80,13 +80,13 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/:topicId`,
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/:topicId`,
|
||||||
component: QualityAssuranceEventsPageComponent,
|
component: QualityAssuranceEventsPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: QualityAssuranceBreadcrumbResolver,
|
breadcrumb: qualityAssuranceBreadcrumbResolver,
|
||||||
openaireQualityAssuranceEventsParams: QualityAssuranceEventsPageResolver,
|
openaireQualityAssuranceEventsParams: qualityAssuranceEventsPageResolver,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
title: 'admin.notifications.event.page.title',
|
title: 'admin.notifications.event.page.title',
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
import { Route } from '@angular/router';
|
import {
|
||||||
|
mapToCanActivate,
|
||||||
|
Route,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { NotifyInfoGuard } from '../../core/coar-notify/notify-info/notify-info.guard';
|
import { notifyInfoGuard } from '../../core/coar-notify/notify-info/notify-info.guard';
|
||||||
import { SiteAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
import { SiteAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
||||||
import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component';
|
import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component';
|
||||||
import { AdminNotifyIncomingComponent } from './admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component';
|
import { AdminNotifyIncomingComponent } from './admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component';
|
||||||
@@ -9,10 +12,10 @@ import { AdminNotifyOutgoingComponent } from './admin-notify-logs/admin-notify-o
|
|||||||
|
|
||||||
export const ROUTES: Route[] = [
|
export const ROUTES: Route[] = [
|
||||||
{
|
{
|
||||||
canActivate: [SiteAdministratorGuard, NotifyInfoGuard],
|
canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard],
|
||||||
path: '',
|
path: '',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
component: AdminNotifyDashboardComponent,
|
component: AdminNotifyDashboardComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
@@ -24,10 +27,10 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'inbound',
|
path: 'inbound',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
component: AdminNotifyIncomingComponent,
|
component: AdminNotifyIncomingComponent,
|
||||||
canActivate: [SiteAdministratorGuard, NotifyInfoGuard],
|
canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard],
|
||||||
data: {
|
data: {
|
||||||
title: 'admin.notify.dashboard.page.title',
|
title: 'admin.notify.dashboard.page.title',
|
||||||
breadcrumbKey: 'admin.notify.dashboard',
|
breadcrumbKey: 'admin.notify.dashboard',
|
||||||
@@ -36,10 +39,10 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'outbound',
|
path: 'outbound',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
component: AdminNotifyOutgoingComponent,
|
component: AdminNotifyOutgoingComponent,
|
||||||
canActivate: [SiteAdministratorGuard, NotifyInfoGuard],
|
canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard],
|
||||||
data: {
|
data: {
|
||||||
title: 'admin.notify.dashboard.page.title',
|
title: 'admin.notify.dashboard.page.title',
|
||||||
breadcrumbKey: 'admin.notify.dashboard',
|
breadcrumbKey: 'admin.notify.dashboard',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { BITSTREAMFORMATS_MODULE_PATH } from './admin-registries-routing-paths';
|
import { BITSTREAMFORMATS_MODULE_PATH } from './admin-registries-routing-paths';
|
||||||
import { MetadataRegistryComponent } from './metadata-registry/metadata-registry.component';
|
import { MetadataRegistryComponent } from './metadata-registry/metadata-registry.component';
|
||||||
import { MetadataSchemaComponent } from './metadata-schema/metadata-schema.component';
|
import { MetadataSchemaComponent } from './metadata-schema/metadata-schema.component';
|
||||||
@@ -8,7 +8,7 @@ import { MetadataSchemaComponent } from './metadata-schema/metadata-schema.compo
|
|||||||
export const ROUTES: Route[] = [
|
export const ROUTES: Route[] = [
|
||||||
{
|
{
|
||||||
path: 'metadata',
|
path: 'metadata',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
data: { title: 'admin.registries.metadata.title', breadcrumbKey: 'admin.registries.metadata' },
|
data: { title: 'admin.registries.metadata.title', breadcrumbKey: 'admin.registries.metadata' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -17,7 +17,7 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':schemaName',
|
path: ':schemaName',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
component: MetadataSchemaComponent,
|
component: MetadataSchemaComponent,
|
||||||
data: { title: 'admin.registries.schema.title', breadcrumbKey: 'admin.registries.schema' },
|
data: { title: 'admin.registries.schema.title', breadcrumbKey: 'admin.registries.schema' },
|
||||||
},
|
},
|
||||||
@@ -25,7 +25,7 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: BITSTREAMFORMATS_MODULE_PATH,
|
path: BITSTREAMFORMATS_MODULE_PATH,
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
loadChildren: () => import('./bitstream-formats/bitstream-formats-routes')
|
loadChildren: () => import('./bitstream-formats/bitstream-formats-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
data: { title: 'admin.registries.bitstream-formats.title', breadcrumbKey: 'admin.registries.bitstream-formats' },
|
data: { title: 'admin.registries.bitstream-formats.title', breadcrumbKey: 'admin.registries.bitstream-formats' },
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { AddBitstreamFormatComponent } from './add-bitstream-format/add-bitstream-format.component';
|
import { AddBitstreamFormatComponent } from './add-bitstream-format/add-bitstream-format.component';
|
||||||
import { BitstreamFormatsComponent } from './bitstream-formats.component';
|
import { BitstreamFormatsComponent } from './bitstream-formats.component';
|
||||||
import { BitstreamFormatsResolver } from './bitstream-formats.resolver';
|
import { bitstreamFormatsResolver } from './bitstream-formats.resolver';
|
||||||
import { EditBitstreamFormatComponent } from './edit-bitstream-format/edit-bitstream-format.component';
|
import { EditBitstreamFormatComponent } from './edit-bitstream-format/edit-bitstream-format.component';
|
||||||
|
|
||||||
const BITSTREAMFORMAT_EDIT_PATH = ':id/edit';
|
const BITSTREAMFORMAT_EDIT_PATH = ':id/edit';
|
||||||
@@ -19,7 +19,7 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: BITSTREAMFORMAT_ADD_PATH,
|
path: BITSTREAMFORMAT_ADD_PATH,
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
providers,
|
providers,
|
||||||
component: AddBitstreamFormatComponent,
|
component: AddBitstreamFormatComponent,
|
||||||
data: { breadcrumbKey: 'admin.registries.bitstream-formats.create' },
|
data: { breadcrumbKey: 'admin.registries.bitstream-formats.create' },
|
||||||
@@ -29,8 +29,8 @@ export const ROUTES: Route[] = [
|
|||||||
providers,
|
providers,
|
||||||
component: EditBitstreamFormatComponent,
|
component: EditBitstreamFormatComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstreamFormat: BitstreamFormatsResolver,
|
bitstreamFormat: bitstreamFormatsResolver,
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { breadcrumbKey: 'admin.registries.bitstream-formats.edit' },
|
data: { breadcrumbKey: 'admin.registries.bitstream-formats.edit' },
|
||||||
},
|
},
|
||||||
|
@@ -31,6 +31,7 @@ import { GroupDataService } from '../../../core/eperson/group-data.service';
|
|||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
||||||
import { BitstreamFormatSupportLevel } from '../../../core/shared/bitstream-format-support-level';
|
import { BitstreamFormatSupportLevel } from '../../../core/shared/bitstream-format-support-level';
|
||||||
|
import { XSRFService } from '../../../core/xsrf/xsrf.service';
|
||||||
import { HostWindowService } from '../../../shared/host-window.service';
|
import { HostWindowService } from '../../../shared/host-window.service';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
||||||
@@ -143,6 +144,7 @@ describe('BitstreamFormatsComponent', () => {
|
|||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: GroupDataService, useValue: groupDataService },
|
{ provide: GroupDataService, useValue: groupDataService },
|
||||||
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
||||||
|
{ provide: XSRFService, useValue: {} },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -12,24 +12,20 @@ import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
|||||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific bitstreamFormat before the route is activated
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class BitstreamFormatsResolver implements Resolve<RemoteData<BitstreamFormat>> {
|
|
||||||
constructor(private bitstreamFormatDataService: BitstreamFormatDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving an bitstreamFormat based on the parameters in the current route
|
* Method for resolving an bitstreamFormat based on the parameters in the current route
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state
|
||||||
|
* @param {BitstreamFormatDataService} bitstreamFormatDataService The BitstreamFormatDataService
|
||||||
* @returns Observable<<RemoteData<BitstreamFormat>> Emits the found bitstreamFormat based on the parameters in the current route,
|
* @returns Observable<<RemoteData<BitstreamFormat>> Emits the found bitstreamFormat based on the parameters in the current route,
|
||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<BitstreamFormat>> {
|
export const bitstreamFormatsResolver: ResolveFn<RemoteData<BitstreamFormat>> = (
|
||||||
return this.bitstreamFormatDataService.findById(route.params.id)
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
bitstreamFormatDataService: BitstreamFormatDataService = inject(BitstreamFormatDataService),
|
||||||
|
): Observable<RemoteData<BitstreamFormat>> => {
|
||||||
|
return bitstreamFormatDataService.findById(route.params.id)
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { FilteredCollectionsComponent } from './filtered-collections/filtered-collections.component';
|
import { FilteredCollectionsComponent } from './filtered-collections/filtered-collections.component';
|
||||||
import { FilteredItemsComponent } from './filtered-items/filtered-items.component';
|
import { FilteredItemsComponent } from './filtered-items/filtered-items.component';
|
||||||
|
|
||||||
export const ROUTES: Route[] = [
|
export const ROUTES: Route[] = [
|
||||||
{
|
{
|
||||||
path: 'collections',
|
path: 'collections',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
data: { title: 'admin.reports.collections.title', breadcrumbKey: 'admin.reports.collections' },
|
data: { title: 'admin.reports.collections.title', breadcrumbKey: 'admin.reports.collections' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -18,7 +18,7 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'queries',
|
path: 'queries',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
data: { title: 'admin.reports.items.title', breadcrumbKey: 'admin.reports.items' },
|
data: { title: 'admin.reports.items.title', breadcrumbKey: 'admin.reports.items' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
||||||
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
|
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
|
||||||
import { ThemedMetadataImportPageComponent } from './admin-import-metadata-page/themed-metadata-import-page.component';
|
import { ThemedMetadataImportPageComponent } from './admin-import-metadata-page/themed-metadata-import-page.component';
|
||||||
@@ -27,37 +27,37 @@ export const ROUTES: Route[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'search',
|
path: 'search',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
component: AdminSearchPageComponent,
|
component: AdminSearchPageComponent,
|
||||||
data: { title: 'admin.search.title', breadcrumbKey: 'admin.search' },
|
data: { title: 'admin.search.title', breadcrumbKey: 'admin.search' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'workflow',
|
path: 'workflow',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
component: AdminWorkflowPageComponent,
|
component: AdminWorkflowPageComponent,
|
||||||
data: { title: 'admin.workflow.title', breadcrumbKey: 'admin.workflow' },
|
data: { title: 'admin.workflow.title', breadcrumbKey: 'admin.workflow' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'curation-tasks',
|
path: 'curation-tasks',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
component: AdminCurationTasksComponent,
|
component: AdminCurationTasksComponent,
|
||||||
data: { title: 'admin.curation-tasks.title', breadcrumbKey: 'admin.curation-tasks' },
|
data: { title: 'admin.curation-tasks.title', breadcrumbKey: 'admin.curation-tasks' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'metadata-import',
|
path: 'metadata-import',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
component: ThemedMetadataImportPageComponent,
|
component: ThemedMetadataImportPageComponent,
|
||||||
data: { title: 'admin.metadata-import.title', breadcrumbKey: 'admin.metadata-import' },
|
data: { title: 'admin.metadata-import.title', breadcrumbKey: 'admin.metadata-import' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'batch-import',
|
path: 'batch-import',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
component: BatchImportPageComponent,
|
component: BatchImportPageComponent,
|
||||||
data: { title: 'admin.batch-import.title', breadcrumbKey: 'admin.batch-import' },
|
data: { title: 'admin.batch-import.title', breadcrumbKey: 'admin.batch-import' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'system-wide-alert',
|
path: 'system-wide-alert',
|
||||||
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
resolve: { breadcrumb: i18nBreadcrumbResolver },
|
||||||
loadChildren: () => import('../system-wide-alert/system-wide-alert-routes').then((m) => m.ROUTES),
|
loadChildren: () => import('../system-wide-alert/system-wide-alert-routes').then((m) => m.ROUTES),
|
||||||
data: { title: 'admin.system-wide-alert.title', breadcrumbKey: 'admin.system-wide-alert' },
|
data: { title: 'admin.system-wide-alert.title', breadcrumbKey: 'admin.system-wide-alert' },
|
||||||
},
|
},
|
||||||
|
@@ -1 +1 @@
|
|||||||
<ds-configuration-search-page configuration="administrativeView" [context]="context"></ds-configuration-search-page>
|
<ds-themed-configuration-search-page configuration="administrativeView" [context]="context"></ds-themed-configuration-search-page>
|
||||||
|
@@ -6,7 +6,7 @@ import {
|
|||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
import { AdminSearchPageComponent } from './admin-search-page.component';
|
import { AdminSearchPageComponent } from './admin-search-page.component';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ describe('AdminSearchPageComponent', () => {
|
|||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
}).overrideComponent(AdminSearchPageComponent, {
|
}).overrideComponent(AdminSearchPageComponent, {
|
||||||
remove: {
|
remove: {
|
||||||
imports: [ConfigurationSearchPageComponent],
|
imports: [ThemedConfigurationSearchPageComponent],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Context } from '../../core/shared/context.model';
|
import { Context } from '../../core/shared/context.model';
|
||||||
import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-admin-search-page',
|
selector: 'ds-admin-search-page',
|
||||||
templateUrl: './admin-search-page.component.html',
|
templateUrl: './admin-search-page.component.html',
|
||||||
styleUrls: ['./admin-search-page.component.scss'],
|
styleUrls: ['./admin-search-page.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [ConfigurationSearchPageComponent],
|
imports: [ThemedConfigurationSearchPageComponent],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -31,12 +31,7 @@ describe('AdminSidebarSectionComponent', () => {
|
|||||||
{ provide: MenuService, useValue: menuService },
|
{ provide: MenuService, useValue: menuService },
|
||||||
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
||||||
],
|
],
|
||||||
}).overrideComponent(AdminSidebarSectionComponent, {
|
}).compileComponents();
|
||||||
set: {
|
|
||||||
entryComponents: [TestComponent],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -70,12 +65,7 @@ describe('AdminSidebarSectionComponent', () => {
|
|||||||
{ provide: MenuService, useValue: menuService },
|
{ provide: MenuService, useValue: menuService },
|
||||||
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
||||||
],
|
],
|
||||||
}).overrideComponent(AdminSidebarSectionComponent, {
|
}).compileComponents();
|
||||||
set: {
|
|
||||||
entryComponents: [TestComponent],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@@ -31,12 +31,7 @@ describe('ExpandableAdminSidebarSectionComponent', () => {
|
|||||||
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
||||||
{ provide: Router, useValue: new RouterStub() },
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
],
|
],
|
||||||
}).overrideComponent(ExpandableAdminSidebarSectionComponent, {
|
}).compileComponents();
|
||||||
set: {
|
|
||||||
entryComponents: [TestComponent],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@@ -1 +1 @@
|
|||||||
<ds-configuration-search-page configuration="supervision" [context]="context"></ds-configuration-search-page>
|
<ds-themed-configuration-search-page configuration="supervision" [context]="context"></ds-themed-configuration-search-page>
|
||||||
|
@@ -5,7 +5,7 @@ import {
|
|||||||
waitForAsync,
|
waitForAsync,
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component';
|
||||||
import { AdminWorkflowPageComponent } from './admin-workflow-page.component';
|
import { AdminWorkflowPageComponent } from './admin-workflow-page.component';
|
||||||
|
|
||||||
describe('AdminSearchPageComponent', () => {
|
describe('AdminSearchPageComponent', () => {
|
||||||
@@ -20,7 +20,7 @@ describe('AdminSearchPageComponent', () => {
|
|||||||
.overrideComponent(AdminWorkflowPageComponent, {
|
.overrideComponent(AdminWorkflowPageComponent, {
|
||||||
remove: {
|
remove: {
|
||||||
imports: [
|
imports: [
|
||||||
ConfigurationSearchPageComponent,
|
ThemedConfigurationSearchPageComponent,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Context } from '../../core/shared/context.model';
|
import { Context } from '../../core/shared/context.model';
|
||||||
import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-admin-workflow-page',
|
selector: 'ds-admin-workflow-page',
|
||||||
templateUrl: './admin-workflow-page.component.html',
|
templateUrl: './admin-workflow-page.component.html',
|
||||||
styleUrls: ['./admin-workflow-page.component.scss'],
|
styleUrls: ['./admin-workflow-page.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [ConfigurationSearchPageComponent],
|
imports: [ThemedConfigurationSearchPageComponent],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,6 +17,7 @@ import { AuthorizationDataService } from '../../../../../core/data/feature-autho
|
|||||||
import { Item } from '../../../../../core/shared/item.model';
|
import { Item } from '../../../../../core/shared/item.model';
|
||||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||||
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
|
||||||
|
import { XSRFService } from '../../../../../core/xsrf/xsrf.service';
|
||||||
import { AuthServiceMock } from '../../../../../shared/mocks/auth.service.mock';
|
import { AuthServiceMock } from '../../../../../shared/mocks/auth.service.mock';
|
||||||
import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock';
|
import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock';
|
||||||
import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock';
|
import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock';
|
||||||
@@ -67,6 +68,7 @@ describe('WorkflowItemSearchResultAdminWorkflowListElementComponent', () => {
|
|||||||
{ provide: ThemeService, useValue: getMockThemeService() },
|
{ provide: ThemeService, useValue: getMockThemeService() },
|
||||||
{ provide: AuthService, useValue: new AuthServiceMock() },
|
{ provide: AuthService, useValue: new AuthServiceMock() },
|
||||||
{ provide: AuthorizationDataService, useValue: {} },
|
{ provide: AuthorizationDataService, useValue: {} },
|
||||||
|
{ provide: XSRFService, useValue: {} },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
})
|
})
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
InMemoryScrollingOptions,
|
InMemoryScrollingOptions,
|
||||||
|
mapToCanActivate,
|
||||||
Route,
|
Route,
|
||||||
RouterConfigOptions,
|
RouterConfigOptions,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -23,18 +24,18 @@ import {
|
|||||||
} from './app-routing-paths';
|
} from './app-routing-paths';
|
||||||
import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routing-paths';
|
import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routing-paths';
|
||||||
import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths';
|
import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths';
|
||||||
import { AuthBlockingGuard } from './core/auth/auth-blocking.guard';
|
import { authBlockingGuard } from './core/auth/auth-blocking.guard';
|
||||||
import { AuthenticatedGuard } from './core/auth/authenticated.guard';
|
import { authenticatedGuard } from './core/auth/authenticated.guard';
|
||||||
import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
|
import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
|
||||||
import { SiteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
import { SiteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
||||||
import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard';
|
import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard';
|
||||||
import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard';
|
import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard';
|
||||||
import { ReloadGuard } from './core/reload/reload.guard';
|
import { reloadGuard } from './core/reload/reload.guard';
|
||||||
import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard';
|
import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard';
|
||||||
import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
||||||
import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component';
|
import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component';
|
||||||
import { ITEM_MODULE_PATH } from './item-page/item-page-routing-paths';
|
import { ITEM_MODULE_PATH } from './item-page/item-page-routing-paths';
|
||||||
import { MenuResolver } from './menu.resolver';
|
import { menuResolver } from './menuResolver';
|
||||||
import { provideSuggestionNotificationsState } from './notifications/provide-suggestion-notifications-state';
|
import { provideSuggestionNotificationsState } from './notifications/provide-suggestion-notifications-state';
|
||||||
import { ThemedPageErrorComponent } from './page-error/themed-page-error.component';
|
import { ThemedPageErrorComponent } from './page-error/themed-page-error.component';
|
||||||
import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component';
|
import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component';
|
||||||
@@ -48,16 +49,16 @@ export const APP_ROUTES: Route[] = [
|
|||||||
{ path: ERROR_PAGE, component: ThemedPageErrorComponent },
|
{ path: ERROR_PAGE, component: ThemedPageErrorComponent },
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
canActivate: [AuthBlockingGuard],
|
canActivate: [authBlockingGuard],
|
||||||
canActivateChild: [ServerCheckGuard],
|
canActivateChild: [ServerCheckGuard],
|
||||||
resolve: [MenuResolver],
|
resolve: [menuResolver],
|
||||||
children: [
|
children: [
|
||||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||||
{
|
{
|
||||||
path: 'reload/:rnd',
|
path: 'reload/:rnd',
|
||||||
component: ThemedPageNotFoundComponent,
|
component: ThemedPageNotFoundComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
canActivate: [ReloadGuard],
|
canActivate: [reloadGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'home',
|
path: 'home',
|
||||||
@@ -65,105 +66,105 @@ export const APP_ROUTES: Route[] = [
|
|||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
data: { showBreadcrumbs: false },
|
data: { showBreadcrumbs: false },
|
||||||
providers: [provideSuggestionNotificationsState()],
|
providers: [provideSuggestionNotificationsState()],
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'community-list',
|
path: 'community-list',
|
||||||
loadChildren: () => import('./community-list-page/community-list-page-routes')
|
loadChildren: () => import('./community-list-page/community-list-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'id',
|
path: 'id',
|
||||||
loadChildren: () => import('./lookup-by-id/lookup-by-id-routes')
|
loadChildren: () => import('./lookup-by-id/lookup-by-id-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'handle',
|
path: 'handle',
|
||||||
loadChildren: () => import('./lookup-by-id/lookup-by-id-routes')
|
loadChildren: () => import('./lookup-by-id/lookup-by-id-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: REGISTER_PATH,
|
path: REGISTER_PATH,
|
||||||
loadChildren: () => import('./register-page/register-page-routes')
|
loadChildren: () => import('./register-page/register-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [SiteRegisterGuard],
|
canActivate: mapToCanActivate([SiteRegisterGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: FORGOT_PASSWORD_PATH,
|
path: FORGOT_PASSWORD_PATH,
|
||||||
loadChildren: () => import('./forgot-password/forgot-password-routes')
|
loadChildren: () => import('./forgot-password/forgot-password-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard, ForgotPasswordCheckGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard, ForgotPasswordCheckGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: COMMUNITY_MODULE_PATH,
|
path: COMMUNITY_MODULE_PATH,
|
||||||
loadChildren: () => import('./community-page/community-page-routes')
|
loadChildren: () => import('./community-page/community-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: COLLECTION_MODULE_PATH,
|
path: COLLECTION_MODULE_PATH,
|
||||||
loadChildren: () => import('./collection-page/collection-page-routes')
|
loadChildren: () => import('./collection-page/collection-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ITEM_MODULE_PATH,
|
path: ITEM_MODULE_PATH,
|
||||||
loadChildren: () => import('./item-page/item-page-routes')
|
loadChildren: () => import('./item-page/item-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'entities/:entity-type',
|
path: 'entities/:entity-type',
|
||||||
loadChildren: () => import('./item-page/item-page-routes')
|
loadChildren: () => import('./item-page/item-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: LEGACY_BITSTREAM_MODULE_PATH,
|
path: LEGACY_BITSTREAM_MODULE_PATH,
|
||||||
loadChildren: () => import('./bitstream-page/bitstream-page-routes')
|
loadChildren: () => import('./bitstream-page/bitstream-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: BITSTREAM_MODULE_PATH,
|
path: BITSTREAM_MODULE_PATH,
|
||||||
loadChildren: () => import('./bitstream-page/bitstream-page-routes')
|
loadChildren: () => import('./bitstream-page/bitstream-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'mydspace',
|
path: 'mydspace',
|
||||||
loadChildren: () => import('./my-dspace-page/my-dspace-page-routes')
|
loadChildren: () => import('./my-dspace-page/my-dspace-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
providers: [provideSuggestionNotificationsState()],
|
providers: [provideSuggestionNotificationsState()],
|
||||||
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'search',
|
path: 'search',
|
||||||
loadChildren: () => import('./search-page/search-page-routes')
|
loadChildren: () => import('./search-page/search-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'browse',
|
path: 'browse',
|
||||||
loadChildren: () => import('./browse-by/browse-by-page-routes')
|
loadChildren: () => import('./browse-by/browse-by-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ADMIN_MODULE_PATH,
|
path: ADMIN_MODULE_PATH,
|
||||||
loadChildren: () => import('./admin/admin-routes')
|
loadChildren: () => import('./admin/admin-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([SiteAdministratorGuard, EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: NOTIFICATIONS_MODULE_PATH,
|
path: NOTIFICATIONS_MODULE_PATH,
|
||||||
loadChildren: () => import('./quality-assurance-notifications-pages/notifications-pages-routes')
|
loadChildren: () => import('./quality-assurance-notifications-pages/notifications-pages-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
providers: [provideSuggestionNotificationsState()],
|
providers: [provideSuggestionNotificationsState()],
|
||||||
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'login',
|
path: 'login',
|
||||||
@@ -180,47 +181,47 @@ export const APP_ROUTES: Route[] = [
|
|||||||
loadChildren: () => import('./submit-page/submit-page-routes')
|
loadChildren: () => import('./submit-page/submit-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
providers: [provideSubmissionState()],
|
providers: [provideSubmissionState()],
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'import-external',
|
path: 'import-external',
|
||||||
loadChildren: () => import('./import-external-page/import-external-page-routes')
|
loadChildren: () => import('./import-external-page/import-external-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'workspaceitems',
|
path: 'workspaceitems',
|
||||||
loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page-routes')
|
loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
providers: [provideSubmissionState()],
|
providers: [provideSubmissionState()],
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: WORKFLOW_ITEM_MODULE_PATH,
|
path: WORKFLOW_ITEM_MODULE_PATH,
|
||||||
providers: [provideSubmissionState()],
|
providers: [provideSubmissionState()],
|
||||||
loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page-routes')
|
loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: PROFILE_MODULE_PATH,
|
path: PROFILE_MODULE_PATH,
|
||||||
loadChildren: () => import('./profile-page/profile-page-routes')
|
loadChildren: () => import('./profile-page/profile-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
providers: [provideSuggestionNotificationsState()],
|
providers: [provideSuggestionNotificationsState()],
|
||||||
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: PROCESS_MODULE_PATH,
|
path: PROCESS_MODULE_PATH,
|
||||||
loadChildren: () => import('./process-page/process-page-routes')
|
loadChildren: () => import('./process-page/process-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: SUGGESTION_MODULE_PATH,
|
path: SUGGESTION_MODULE_PATH,
|
||||||
loadChildren: () => import('./suggestions-page/suggestions-page-routes')
|
loadChildren: () => import('./suggestions-page/suggestions-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
providers: [provideSuggestionNotificationsState()],
|
providers: [provideSuggestionNotificationsState()],
|
||||||
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: INFO_MODULE_PATH,
|
path: INFO_MODULE_PATH,
|
||||||
@@ -229,7 +230,7 @@ export const APP_ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: REQUEST_COPY_MODULE_PATH,
|
path: REQUEST_COPY_MODULE_PATH,
|
||||||
loadChildren: () => import('./request-copy/request-copy-routes').then((m) => m.ROUTES),
|
loadChildren: () => import('./request-copy/request-copy-routes').then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: FORBIDDEN_PATH,
|
path: FORBIDDEN_PATH,
|
||||||
@@ -239,7 +240,7 @@ export const APP_ROUTES: Route[] = [
|
|||||||
path: 'statistics',
|
path: 'statistics',
|
||||||
loadChildren: () => import('./statistics-page/statistics-page-routes')
|
loadChildren: () => import('./statistics-page/statistics-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: HEALTH_PAGE_PATH,
|
path: HEALTH_PAGE_PATH,
|
||||||
@@ -249,13 +250,13 @@ export const APP_ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: ACCESS_CONTROL_MODULE_PATH,
|
path: ACCESS_CONTROL_MODULE_PATH,
|
||||||
loadChildren: () => import('./access-control/access-control-routes').then((m) => m.ROUTES),
|
loadChildren: () => import('./access-control/access-control-routes').then((m) => m.ROUTES),
|
||||||
canActivate: [GroupAdministratorGuard, EndUserAgreementCurrentUserGuard],
|
canActivate: mapToCanActivate([GroupAdministratorGuard, EndUserAgreementCurrentUserGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'subscriptions',
|
path: 'subscriptions',
|
||||||
loadChildren: () => import('./subscriptions-page/subscriptions-page-routes')
|
loadChildren: () => import('./subscriptions-page/subscriptions-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
},
|
},
|
||||||
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent },
|
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent },
|
||||||
],
|
],
|
||||||
|
@@ -34,9 +34,8 @@ export function getBitstreamRequestACopyRoute(item, bitstream): { routerLink: st
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export const COAR_NOTIFY_SUPPORT = 'coar-notify-support';
|
|
||||||
|
|
||||||
export const HOME_PAGE_PATH = 'admin';
|
export const HOME_PAGE_PATH = 'home';
|
||||||
|
|
||||||
export function getHomePageRoute() {
|
export function getHomePageRoute() {
|
||||||
return `/${HOME_PAGE_PATH}`;
|
return `/${HOME_PAGE_PATH}`;
|
||||||
|
@@ -35,6 +35,7 @@ import {
|
|||||||
NativeWindowRef,
|
NativeWindowRef,
|
||||||
NativeWindowService,
|
NativeWindowService,
|
||||||
} from './core/services/window.service';
|
} from './core/services/window.service';
|
||||||
|
import { ThemedRootComponent } from './root/themed-root.component';
|
||||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||||
import { HostWindowService } from './shared/host-window.service';
|
import { HostWindowService } from './shared/host-window.service';
|
||||||
import { MenuService } from './shared/menu/menu.service';
|
import { MenuService } from './shared/menu/menu.service';
|
||||||
@@ -84,7 +85,6 @@ describe('App component', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
declarations: [AppComponent], // declare the test component
|
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: NativeWindowService, useValue: new NativeWindowRef() },
|
{ provide: NativeWindowService, useValue: new NativeWindowRef() },
|
||||||
{ provide: MetadataService, useValue: new MetadataServiceMock() },
|
{ provide: MetadataService, useValue: new MetadataServiceMock() },
|
||||||
@@ -109,7 +109,13 @@ describe('App component', () => {
|
|||||||
|
|
||||||
// waitForAsync beforeEach
|
// waitForAsync beforeEach
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
return TestBed.configureTestingModule(getDefaultTestBedConf());
|
return TestBed.configureTestingModule(getDefaultTestBedConf()).overrideComponent(
|
||||||
|
AppComponent, {
|
||||||
|
remove: {
|
||||||
|
imports: [ ThemedRootComponent ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// synchronous beforeEach
|
// synchronous beforeEach
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
AsyncPipe,
|
||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
isPlatformBrowser,
|
isPlatformBrowser,
|
||||||
} from '@angular/common';
|
} from '@angular/common';
|
||||||
@@ -44,6 +45,7 @@ import {
|
|||||||
NativeWindowService,
|
NativeWindowService,
|
||||||
} from './core/services/window.service';
|
} from './core/services/window.service';
|
||||||
import { distinctNext } from './core/shared/distinct-next';
|
import { distinctNext } from './core/shared/distinct-next';
|
||||||
|
import { ThemedRootComponent } from './root/themed-root.component';
|
||||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||||
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
||||||
import { CSSVariableService } from './shared/sass-helper/css-variable.service';
|
import { CSSVariableService } from './shared/sass-helper/css-variable.service';
|
||||||
@@ -55,6 +57,11 @@ import { ThemeService } from './shared/theme-support/theme.service';
|
|||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss'],
|
styleUrls: ['./app.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ThemedRootComponent,
|
||||||
|
AsyncPipe,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, AfterViewInit {
|
export class AppComponent implements OnInit, AfterViewInit {
|
||||||
notificationOptions;
|
notificationOptions;
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
APP_BASE_HREF,
|
APP_BASE_HREF,
|
||||||
CommonModule,
|
|
||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
} from '@angular/common';
|
} from '@angular/common';
|
||||||
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
import {
|
import {
|
||||||
HTTP_INTERCEPTORS,
|
ApplicationConfig,
|
||||||
HttpClientModule,
|
importProvidersFrom,
|
||||||
} from '@angular/common/http';
|
} from '@angular/core';
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import {
|
import {
|
||||||
NoPreloading,
|
NoPreloading,
|
||||||
provideRouter,
|
provideRouter,
|
||||||
@@ -29,7 +27,6 @@ import {
|
|||||||
StoreModule,
|
StoreModule,
|
||||||
USER_PROVIDED_META_REDUCERS,
|
USER_PROVIDED_META_REDUCERS,
|
||||||
} from '@ngrx/store';
|
} from '@ngrx/store';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
||||||
import { NgxMaskModule } from 'ngx-mask';
|
import { NgxMaskModule } from 'ngx-mask';
|
||||||
|
|
||||||
@@ -40,7 +37,6 @@ import {
|
|||||||
import { StoreDevModules } from '../config/store/devtools';
|
import { StoreDevModules } from '../config/store/devtools';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { EagerThemesModule } from '../themes/eager-themes.module';
|
import { EagerThemesModule } from '../themes/eager-themes.module';
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { appEffects } from './app.effects';
|
import { appEffects } from './app.effects';
|
||||||
import {
|
import {
|
||||||
appMetaReducers,
|
appMetaReducers,
|
||||||
@@ -68,7 +64,6 @@ import { ClientCookieService } from './core/services/client-cookie.service';
|
|||||||
import { ListableModule } from './core/shared/listable.module';
|
import { ListableModule } from './core/shared/listable.module';
|
||||||
import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor';
|
import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor';
|
||||||
import { RootModule } from './root.module';
|
import { RootModule } from './root.module';
|
||||||
import { ThemedRootComponent } from './root/themed-root.component';
|
|
||||||
import { AUTH_METHOD_FOR_DECORATOR_MAP } from './shared/log-in/methods/log-in.methods-decorator';
|
import { AUTH_METHOD_FOR_DECORATOR_MAP } from './shared/log-in/methods/log-in.methods-decorator';
|
||||||
import { METADATA_REPRESENTATION_COMPONENT_DECORATOR_MAP } from './shared/metadata-representation/metadata-representation.decorator';
|
import { METADATA_REPRESENTATION_COMPONENT_DECORATOR_MAP } from './shared/metadata-representation/metadata-representation.decorator';
|
||||||
import {
|
import {
|
||||||
@@ -92,12 +87,12 @@ export function getMetaReducers(appConfig: AppConfig): MetaReducer<AppState>[] {
|
|||||||
return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
|
return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IMPORTS = [
|
export const commonAppConfig: ApplicationConfig = {
|
||||||
CommonModule,
|
providers: [
|
||||||
HttpClientModule,
|
importProvidersFrom(
|
||||||
ScrollToModule.forRoot(),
|
ScrollToModule.forRoot(),
|
||||||
NgbModule,
|
NgbModule,
|
||||||
TranslateModule.forRoot(),
|
// TranslateModule.forRoot(),
|
||||||
EffectsModule.forRoot(appEffects),
|
EffectsModule.forRoot(appEffects),
|
||||||
StoreModule.forRoot(appReducers, storeModuleConfig),
|
StoreModule.forRoot(appReducers, storeModuleConfig),
|
||||||
StoreRouterConnectingModule.forRoot(),
|
StoreRouterConnectingModule.forRoot(),
|
||||||
@@ -105,9 +100,8 @@ const IMPORTS = [
|
|||||||
EagerThemesModule,
|
EagerThemesModule,
|
||||||
RootModule,
|
RootModule,
|
||||||
ListableModule.withEntryComponents(),
|
ListableModule.withEntryComponents(),
|
||||||
];
|
NgxMaskModule.forRoot(),
|
||||||
|
),
|
||||||
const PROVIDERS = [
|
|
||||||
provideRouter(
|
provideRouter(
|
||||||
APP_ROUTES,
|
APP_ROUTES,
|
||||||
withRouterConfig(APP_ROUTING_CONF),
|
withRouterConfig(APP_ROUTING_CONF),
|
||||||
@@ -156,33 +150,16 @@ const PROVIDERS = [
|
|||||||
},
|
},
|
||||||
// register the dynamic matcher used by form. MUST be provided by the app module
|
// register the dynamic matcher used by form. MUST be provided by the app module
|
||||||
...DYNAMIC_MATCHER_PROVIDERS,
|
...DYNAMIC_MATCHER_PROVIDERS,
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
AppComponent,
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
BrowserModule.withServerTransition({ appId: 'dspace-angular' }),
|
|
||||||
...IMPORTS,
|
|
||||||
NgxMaskModule.forRoot(),
|
|
||||||
ThemedRootComponent,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
...PROVIDERS,
|
|
||||||
provideCore(),
|
provideCore(),
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
};
|
||||||
})
|
|
||||||
export class AppModule {
|
|
||||||
|
|
||||||
/* Use models object so all decorators are actually called */
|
|
||||||
modelList = models;
|
/* Use models object so all decorators are actually called */
|
||||||
workflowTasks = WORKFLOW_TASK_OPTION_DECORATOR_MAP;
|
const modelList = models;
|
||||||
advancedWorfklowTasks = ADVANCED_WORKFLOW_TASK_OPTION_DECORATOR_MAP;
|
const workflowTasks = WORKFLOW_TASK_OPTION_DECORATOR_MAP;
|
||||||
metadataRepresentations = METADATA_REPRESENTATION_COMPONENT_DECORATOR_MAP;
|
const advancedWorfklowTasks = ADVANCED_WORKFLOW_TASK_OPTION_DECORATOR_MAP;
|
||||||
startsWithDecoratorMap = STARTS_WITH_DECORATOR_MAP;
|
const metadataRepresentations = METADATA_REPRESENTATION_COMPONENT_DECORATOR_MAP;
|
||||||
browseByDecoratorMap = BROWSE_BY_DECORATOR_MAP;
|
const startsWithDecoratorMap = STARTS_WITH_DECORATOR_MAP;
|
||||||
authMethodForDecoratorMap = AUTH_METHOD_FOR_DECORATOR_MAP;
|
const browseByDecoratorMap = BROWSE_BY_DECORATOR_MAP;
|
||||||
}
|
const authMethodForDecoratorMap = AUTH_METHOD_FOR_DECORATOR_MAP;
|
@@ -1,17 +1,17 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { authenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { BitstreamBreadcrumbResolver } from '../core/breadcrumbs/bitstream-breadcrumb.resolver';
|
import { bitstreamBreadcrumbResolver } from '../core/breadcrumbs/bitstream-breadcrumb.resolver';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { ResourcePolicyCreateComponent } from '../shared/resource-policies/create/resource-policy-create.component';
|
import { ResourcePolicyCreateComponent } from '../shared/resource-policies/create/resource-policy-create.component';
|
||||||
import { ResourcePolicyEditComponent } from '../shared/resource-policies/edit/resource-policy-edit.component';
|
import { ResourcePolicyEditComponent } from '../shared/resource-policies/edit/resource-policy-edit.component';
|
||||||
import { ResourcePolicyResolver } from '../shared/resource-policies/resolvers/resource-policy.resolver';
|
import { resourcePolicyResolver } from '../shared/resource-policies/resolvers/resource-policy.resolver';
|
||||||
import { ResourcePolicyTargetResolver } from '../shared/resource-policies/resolvers/resource-policy-target.resolver';
|
import { resourcePolicyTargetResolver } from '../shared/resource-policies/resolvers/resource-policy-target.resolver';
|
||||||
import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component';
|
import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component';
|
||||||
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
|
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
|
||||||
import { BitstreamPageResolver } from './bitstream-page.resolver';
|
import { bitstreamPageResolver } from './bitstream-page.resolver';
|
||||||
import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component';
|
import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component';
|
||||||
import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
import { legacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
||||||
|
|
||||||
const EDIT_BITSTREAM_PATH = ':id/edit';
|
const EDIT_BITSTREAM_PATH = ':id/edit';
|
||||||
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
||||||
@@ -25,7 +25,7 @@ export const ROUTES: Route[] = [
|
|||||||
path: 'handle/:prefix/:suffix/:filename',
|
path: 'handle/:prefix/:suffix/:filename',
|
||||||
component: BitstreamDownloadPageComponent,
|
component: BitstreamDownloadPageComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstream: LegacyBitstreamUrlResolver,
|
bitstream: legacyBitstreamUrlResolver,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ export const ROUTES: Route[] = [
|
|||||||
path: ':prefix/:suffix/:sequence_id/:filename',
|
path: ':prefix/:suffix/:sequence_id/:filename',
|
||||||
component: BitstreamDownloadPageComponent,
|
component: BitstreamDownloadPageComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstream: LegacyBitstreamUrlResolver,
|
bitstream: legacyBitstreamUrlResolver,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -41,17 +41,17 @@ export const ROUTES: Route[] = [
|
|||||||
path: ':id/download',
|
path: ':id/download',
|
||||||
component: BitstreamDownloadPageComponent,
|
component: BitstreamDownloadPageComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstream: BitstreamPageResolver,
|
bitstream: bitstreamPageResolver,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: EDIT_BITSTREAM_PATH,
|
path: EDIT_BITSTREAM_PATH,
|
||||||
component: ThemedEditBitstreamPageComponent,
|
component: ThemedEditBitstreamPageComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstream: BitstreamPageResolver,
|
bitstream: bitstreamPageResolver,
|
||||||
breadcrumb: BitstreamBreadcrumbResolver,
|
breadcrumb: bitstreamBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH,
|
path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH,
|
||||||
@@ -59,7 +59,7 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
resolve: {
|
resolve: {
|
||||||
resourcePolicyTarget: ResourcePolicyTargetResolver,
|
resourcePolicyTarget: resourcePolicyTargetResolver,
|
||||||
},
|
},
|
||||||
component: ResourcePolicyCreateComponent,
|
component: ResourcePolicyCreateComponent,
|
||||||
data: { title: 'resource-policies.create.page.title', showBreadcrumbs: true },
|
data: { title: 'resource-policies.create.page.title', showBreadcrumbs: true },
|
||||||
@@ -67,8 +67,8 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'edit',
|
path: 'edit',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
resourcePolicy: ResourcePolicyResolver,
|
resourcePolicy: resourcePolicyResolver,
|
||||||
},
|
},
|
||||||
component: ResourcePolicyEditComponent,
|
component: ResourcePolicyEditComponent,
|
||||||
data: { breadcrumbKey: 'item.edit', title: 'resource-policies.edit.page.title', showBreadcrumbs: true },
|
data: { breadcrumbKey: 'item.edit', title: 'resource-policies.edit.page.title', showBreadcrumbs: true },
|
||||||
@@ -76,8 +76,8 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
resolve: {
|
resolve: {
|
||||||
bitstream: BitstreamPageResolver,
|
bitstream: bitstreamPageResolver,
|
||||||
breadcrumb: BitstreamBreadcrumbResolver,
|
breadcrumb: bitstreamBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
component: BitstreamAuthorizationsComponent,
|
component: BitstreamAuthorizationsComponent,
|
||||||
data: { title: 'bitstream.edit.authorizations.title', showBreadcrumbs: true },
|
data: { title: 'bitstream.edit.authorizations.title', showBreadcrumbs: true },
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -25,32 +25,20 @@ export const BITSTREAM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Bitstream>[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific bitstream before the route is activated
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class BitstreamPageResolver implements Resolve<RemoteData<Bitstream>> {
|
|
||||||
constructor(private bitstreamService: BitstreamDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a bitstream based on the parameters in the current route
|
* Method for resolving a bitstream based on the parameters in the current route
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {BitstreamDataService} bitstreamService
|
||||||
* @returns Observable<<RemoteData<Item>> Emits the found bitstream based on the parameters in the current route,
|
* @returns Observable<<RemoteData<Item>> Emits the found bitstream based on the parameters in the current route,
|
||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Bitstream>> {
|
export const bitstreamPageResolver: ResolveFn<RemoteData<Bitstream>> = (
|
||||||
return this.bitstreamService.findById(route.params.id, true, false, ...this.followLinks)
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
bitstreamService: BitstreamDataService = inject(BitstreamDataService),
|
||||||
|
): Observable<RemoteData<Bitstream>> => {
|
||||||
|
return bitstreamService.findById(route.params.id, true, false, ...BITSTREAM_PAGE_LINKS_TO_FOLLOW)
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
/**
|
|
||||||
* Method that returns the follow links to already resolve
|
|
||||||
* The self links defined in this list are expected to be requested somewhere in the near future
|
|
||||||
* Requesting them as embeds will limit the number of requests
|
|
||||||
*/
|
|
||||||
get followLinks(): FollowLinkConfig<Bitstream>[] {
|
|
||||||
return BITSTREAM_PAGE_LINKS_TO_FOLLOW;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -4,10 +4,10 @@ import { TestScheduler } from 'rxjs/testing';
|
|||||||
import { BitstreamDataService } from '../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../core/data/bitstream-data.service';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { RequestEntryState } from '../core/data/request-entry-state.model';
|
import { RequestEntryState } from '../core/data/request-entry-state.model';
|
||||||
import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
import { legacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
||||||
|
|
||||||
describe(`LegacyBitstreamUrlResolver`, () => {
|
describe(`legacyBitstreamUrlResolver`, () => {
|
||||||
let resolver: LegacyBitstreamUrlResolver;
|
let resolver: any;
|
||||||
let bitstreamDataService: BitstreamDataService;
|
let bitstreamDataService: BitstreamDataService;
|
||||||
let testScheduler;
|
let testScheduler;
|
||||||
let remoteDataMocks;
|
let remoteDataMocks;
|
||||||
@@ -33,7 +33,7 @@ describe(`LegacyBitstreamUrlResolver`, () => {
|
|||||||
bitstreamDataService = {
|
bitstreamDataService = {
|
||||||
findByItemHandle: () => undefined,
|
findByItemHandle: () => undefined,
|
||||||
} as any;
|
} as any;
|
||||||
resolver = new LegacyBitstreamUrlResolver(bitstreamDataService);
|
resolver = legacyBitstreamUrlResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`resolve`, () => {
|
describe(`resolve`, () => {
|
||||||
@@ -51,7 +51,7 @@ describe(`LegacyBitstreamUrlResolver`, () => {
|
|||||||
});
|
});
|
||||||
it(`should call findByItemHandle with the handle, sequence id, and filename from the route`, () => {
|
it(`should call findByItemHandle with the handle, sequence id, and filename from the route`, () => {
|
||||||
testScheduler.run(() => {
|
testScheduler.run(() => {
|
||||||
resolver.resolve(route, state);
|
resolver(route, state, bitstreamDataService);
|
||||||
expect(bitstreamDataService.findByItemHandle).toHaveBeenCalledWith(
|
expect(bitstreamDataService.findByItemHandle).toHaveBeenCalledWith(
|
||||||
`${route.params.prefix}/${route.params.suffix}`,
|
`${route.params.prefix}/${route.params.suffix}`,
|
||||||
route.params.sequence_id,
|
route.params.sequence_id,
|
||||||
@@ -78,7 +78,7 @@ describe(`LegacyBitstreamUrlResolver`, () => {
|
|||||||
});
|
});
|
||||||
it(`should call findByItemHandle with the handle and filename from the route, and the sequence ID from the queryParams`, () => {
|
it(`should call findByItemHandle with the handle and filename from the route, and the sequence ID from the queryParams`, () => {
|
||||||
testScheduler.run(() => {
|
testScheduler.run(() => {
|
||||||
resolver.resolve(route, state);
|
resolver(route, state, bitstreamDataService);
|
||||||
expect(bitstreamDataService.findByItemHandle).toHaveBeenCalledWith(
|
expect(bitstreamDataService.findByItemHandle).toHaveBeenCalledWith(
|
||||||
`${route.params.prefix}/${route.params.suffix}`,
|
`${route.params.prefix}/${route.params.suffix}`,
|
||||||
route.queryParams.sequenceId,
|
route.queryParams.sequenceId,
|
||||||
@@ -100,7 +100,7 @@ describe(`LegacyBitstreamUrlResolver`, () => {
|
|||||||
});
|
});
|
||||||
it(`should call findByItemHandle with the handle, and filename from the route`, () => {
|
it(`should call findByItemHandle with the handle, and filename from the route`, () => {
|
||||||
testScheduler.run(() => {
|
testScheduler.run(() => {
|
||||||
resolver.resolve(route, state);
|
resolver(route, state, bitstreamDataService);
|
||||||
expect(bitstreamDataService.findByItemHandle).toHaveBeenCalledWith(
|
expect(bitstreamDataService.findByItemHandle).toHaveBeenCalledWith(
|
||||||
`${route.params.prefix}/${route.params.suffix}`,
|
`${route.params.prefix}/${route.params.suffix}`,
|
||||||
undefined,
|
undefined,
|
||||||
@@ -123,7 +123,7 @@ describe(`LegacyBitstreamUrlResolver`, () => {
|
|||||||
c: remoteDataMocks.Error,
|
c: remoteDataMocks.Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
expectObservable(resolver.resolve(route, state)).toBe(expected, values);
|
expectObservable(resolver(route, state, bitstreamDataService)).toBe(expected, values);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it(`...succeeded`, () => {
|
it(`...succeeded`, () => {
|
||||||
@@ -138,7 +138,7 @@ describe(`LegacyBitstreamUrlResolver`, () => {
|
|||||||
c: remoteDataMocks.Success,
|
c: remoteDataMocks.Success,
|
||||||
};
|
};
|
||||||
|
|
||||||
expectObservable(resolver.resolve(route, state)).toBe(expected, values);
|
expectObservable(resolver(route, state, bitstreamDataService)).toBe(expected, values);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -13,26 +13,20 @@ import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
|||||||
import { hasNoValue } from '../shared/empty.util';
|
import { hasNoValue } from '../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class resolves a bitstream based on the DSpace 6 XMLUI or JSPUI bitstream download URLs
|
|
||||||
*/
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class LegacyBitstreamUrlResolver implements Resolve<RemoteData<Bitstream>> {
|
|
||||||
constructor(protected bitstreamDataService: BitstreamDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a bitstream based on the handle of the item, and the sequence id or the filename of the
|
* Resolve a bitstream based on the handle of the item, and the sequence id or the filename of the
|
||||||
* bitstream
|
* bitstream
|
||||||
*
|
*
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {BitstreamDataService} bitstreamDataService
|
||||||
* @returns Observable<<RemoteData<Item>> Emits the found bitstream based on the parameters in
|
* @returns Observable<<RemoteData<Item>> Emits the found bitstream based on the parameters in
|
||||||
* current route, or an error if something went wrong
|
* current route, or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
|
export const legacyBitstreamUrlResolver: ResolveFn<RemoteData<Bitstream>> = (
|
||||||
Observable<RemoteData<Bitstream>> {
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
bitstreamDataService: BitstreamDataService = inject(BitstreamDataService),
|
||||||
|
): Observable<RemoteData<Bitstream>> => {
|
||||||
const prefix = route.params.prefix;
|
const prefix = route.params.prefix;
|
||||||
const suffix = route.params.suffix;
|
const suffix = route.params.suffix;
|
||||||
const filename = route.params.filename;
|
const filename = route.params.filename;
|
||||||
@@ -42,12 +36,11 @@ export class LegacyBitstreamUrlResolver implements Resolve<RemoteData<Bitstream>
|
|||||||
sequenceId = route.queryParams.sequenceId;
|
sequenceId = route.queryParams.sequenceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.bitstreamDataService.findByItemHandle(
|
return bitstreamDataService.findByItemHandle(
|
||||||
`${prefix}/${suffix}`,
|
`${prefix}/${suffix}`,
|
||||||
sequenceId,
|
sequenceId,
|
||||||
filename,
|
filename,
|
||||||
).pipe(
|
).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -19,30 +20,28 @@ import {
|
|||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for a DSpaceObject on a browse by page
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class BrowseByDSOBreadcrumbResolver {
|
|
||||||
constructor(protected breadcrumbService: DSOBreadcrumbsService, protected dataService: DSpaceObjectDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a breadcrumb config object
|
* Method for resolving a breadcrumb config object
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {DSOBreadcrumbsService} breadcrumbService
|
||||||
|
* @param {DSpaceObjectDataService} dataService
|
||||||
* @returns BreadcrumbConfig object
|
* @returns BreadcrumbConfig object
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<Community | Collection>> {
|
export const browseByDSOBreadcrumbResolver: ResolveFn<BreadcrumbConfig<Community | Collection>> = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
breadcrumbService: DSOBreadcrumbsService = inject(DSOBreadcrumbsService),
|
||||||
|
dataService: DSpaceObjectDataService = inject(DSpaceObjectDataService),
|
||||||
|
): Observable<BreadcrumbConfig<Community | Collection>> => {
|
||||||
const uuid = route.queryParams.scope;
|
const uuid = route.queryParams.scope;
|
||||||
if (hasValue(uuid)) {
|
if (hasValue(uuid)) {
|
||||||
return this.dataService.findById(uuid).pipe(
|
return dataService.findById(uuid).pipe(
|
||||||
getFirstSucceededRemoteData(),
|
getFirstSucceededRemoteData(),
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
map((object: Community | Collection) => {
|
map((object: Community | Collection) => {
|
||||||
return { provider: this.breadcrumbService, key: object, url: getDSORoute(object) };
|
return { provider: breadcrumbService, key: object, url: getDSORoute(object) };
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -6,12 +6,12 @@ import {
|
|||||||
createSuccessfulRemoteDataObject$,
|
createSuccessfulRemoteDataObject$,
|
||||||
} from '../shared/remote-data.utils';
|
} from '../shared/remote-data.utils';
|
||||||
import { RouterStub } from '../shared/testing/router.stub';
|
import { RouterStub } from '../shared/testing/router.stub';
|
||||||
import { BrowseByGuard } from './browse-by-guard';
|
import { browseByGuard } from './browse-by-guard';
|
||||||
import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type';
|
import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type';
|
||||||
|
|
||||||
describe('BrowseByGuard', () => {
|
describe('browseByGuard', () => {
|
||||||
describe('canActivate', () => {
|
describe('canActivate', () => {
|
||||||
let guard: BrowseByGuard;
|
let guard: any;
|
||||||
let translateService: any;
|
let translateService: any;
|
||||||
let browseDefinitionService: any;
|
let browseDefinitionService: any;
|
||||||
let router: any;
|
let router: any;
|
||||||
@@ -35,7 +35,7 @@ describe('BrowseByGuard', () => {
|
|||||||
|
|
||||||
router = new RouterStub() as any;
|
router = new RouterStub() as any;
|
||||||
|
|
||||||
guard = new BrowseByGuard(translateService, browseDefinitionService, router);
|
guard = browseByGuard;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true, and sets up the data correctly, with a scope and value', () => {
|
it('should return true, and sets up the data correctly, with a scope and value', () => {
|
||||||
@@ -53,7 +53,7 @@ describe('BrowseByGuard', () => {
|
|||||||
value,
|
value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
guard.canActivate(scopedRoute as any, undefined)
|
guard(scopedRoute as any, undefined, browseDefinitionService, router, translateService)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) => {
|
(canActivate) => {
|
||||||
@@ -86,7 +86,7 @@ describe('BrowseByGuard', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
guard.canActivate(scopedNoValueRoute as any, undefined)
|
guard(scopedNoValueRoute, undefined, browseDefinitionService, router, translateService)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) => {
|
(canActivate) => {
|
||||||
@@ -123,7 +123,7 @@ describe('BrowseByGuard', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
guard.canActivate(scopedNoValueRoute as any, undefined).pipe(
|
guard(scopedNoValueRoute as any, undefined, browseDefinitionService, router, translateService).pipe(
|
||||||
first(),
|
first(),
|
||||||
).subscribe((canActivate) => {
|
).subscribe((canActivate) => {
|
||||||
const result = {
|
const result = {
|
||||||
@@ -154,7 +154,8 @@ describe('BrowseByGuard', () => {
|
|||||||
value,
|
value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
guard.canActivate(route as any, undefined)
|
|
||||||
|
guard(route as any, undefined, browseDefinitionService, router, translateService)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) => {
|
(canActivate) => {
|
||||||
@@ -189,7 +190,8 @@ describe('BrowseByGuard', () => {
|
|||||||
value,
|
value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
guard.canActivate(scopedRoute as any, undefined)
|
|
||||||
|
guard(scopedRoute as any, undefined, browseDefinitionService, router, translateService)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((canActivate) => {
|
.subscribe((canActivate) => {
|
||||||
expect(router.navigate).toHaveBeenCalled();
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
CanActivateFn,
|
||||||
Data,
|
Data,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
@@ -26,25 +26,18 @@ import {
|
|||||||
hasValue,
|
hasValue,
|
||||||
} from '../shared/empty.util';
|
} from '../shared/empty.util';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
export const browseByGuard: CanActivateFn = (
|
||||||
/**
|
route: ActivatedRouteSnapshot,
|
||||||
* A guard taking care of the correct route.data being set for the Browse-By components
|
state: RouterStateSnapshot,
|
||||||
*/
|
browseDefinitionService: BrowseDefinitionDataService = inject(BrowseDefinitionDataService),
|
||||||
export class BrowseByGuard implements CanActivate {
|
router: Router = inject(Router),
|
||||||
|
translate: TranslateService = inject(TranslateService),
|
||||||
constructor(
|
): Observable<boolean> => {
|
||||||
protected translate: TranslateService,
|
|
||||||
protected browseDefinitionService: BrowseDefinitionDataService,
|
|
||||||
protected router: Router,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
|
||||||
const title = route.data.title;
|
const title = route.data.title;
|
||||||
const id = route.params.id || route.queryParams.id || route.data.id;
|
const id = route.params.id || route.queryParams.id || route.data.id;
|
||||||
let browseDefinition$: Observable<BrowseDefinition | undefined>;
|
let browseDefinition$: Observable<BrowseDefinition | undefined>;
|
||||||
if (hasNoValue(route.data.browseDefinition) && hasValue(id)) {
|
if (hasNoValue(route.data.browseDefinition) && hasValue(id)) {
|
||||||
browseDefinition$ = this.browseDefinitionService.findById(id).pipe(
|
browseDefinition$ = browseDefinitionService.findById(id).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((browseDefinitionRD: RemoteData<BrowseDefinition>) => browseDefinitionRD.payload),
|
map((browseDefinitionRD: RemoteData<BrowseDefinition>) => browseDefinitionRD.payload),
|
||||||
);
|
);
|
||||||
@@ -53,21 +46,21 @@ export class BrowseByGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
const scope = route.queryParams.scope ?? route.parent?.params.id;
|
const scope = route.queryParams.scope ?? route.parent?.params.id;
|
||||||
const value = route.queryParams.value;
|
const value = route.queryParams.value;
|
||||||
const metadataTranslated = this.translate.instant(`browse.metadata.${id}`);
|
const metadataTranslated = translate.instant(`browse.metadata.${id}`);
|
||||||
return browseDefinition$.pipe(
|
return browseDefinition$.pipe(
|
||||||
switchMap((browseDefinition: BrowseDefinition | undefined) => {
|
switchMap((browseDefinition: BrowseDefinition | undefined) => {
|
||||||
if (hasValue(browseDefinition)) {
|
if (hasValue(browseDefinition)) {
|
||||||
route.data = this.createData(title, id, browseDefinition, metadataTranslated, value, route, scope);
|
route.data = createData(title, id, browseDefinition, metadataTranslated, value, route, scope);
|
||||||
return observableOf(true);
|
return observableOf(true);
|
||||||
} else {
|
} else {
|
||||||
void this.router.navigate([PAGE_NOT_FOUND_PATH]);
|
void router.navigate([PAGE_NOT_FOUND_PATH]);
|
||||||
return observableOf(false);
|
return observableOf(false);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
private createData(title: string, id: string, browseDefinition: BrowseDefinition, field: string, value: string, route: ActivatedRouteSnapshot, scope: string): Data {
|
function createData(title: string, id: string, browseDefinition: BrowseDefinition, field: string, value: string, route: ActivatedRouteSnapshot, scope: string): Data {
|
||||||
return Object.assign({}, route.data, {
|
return Object.assign({}, route.data, {
|
||||||
title: title,
|
title: title,
|
||||||
id: id,
|
id: id,
|
||||||
@@ -76,5 +69,4 @@ export class BrowseByGuard implements CanActivate {
|
|||||||
value: hasValue(value) ? `"${value}"` : '',
|
value: hasValue(value) ? `"${value}"` : '',
|
||||||
scope: scope,
|
scope: scope,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,32 +1,23 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class resolves a BreadcrumbConfig object with an i18n key string for a route
|
|
||||||
* It adds the metadata field of the current browse-by page
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class BrowseByI18nBreadcrumbResolver extends I18nBreadcrumbResolver {
|
|
||||||
constructor(protected breadcrumbService: I18nBreadcrumbsService) {
|
|
||||||
super(breadcrumbService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a browse-by i18n breadcrumb configuration object
|
* Method for resolving a browse-by i18n breadcrumb configuration object
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
* @returns BreadcrumbConfig object for a browse-by page
|
* @returns BreadcrumbConfig object for a browse-by page
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
export const browseByI18nBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
): BreadcrumbConfig<string> => {
|
||||||
const extendedBreadcrumbKey = route.data.breadcrumbKey + '.' + route.params.id;
|
const extendedBreadcrumbKey = route.data.breadcrumbKey + '.' + route.params.id;
|
||||||
route.data = Object.assign({}, route.data, { breadcrumbKey: extendedBreadcrumbKey });
|
route.data = Object.assign({}, route.data, { breadcrumbKey: extendedBreadcrumbKey });
|
||||||
return super.resolve(route, state);
|
return i18nBreadcrumbResolver(route, state) as BreadcrumbConfig<string>;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,24 +1,24 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
import { dsoEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||||
import { BrowseByDSOBreadcrumbResolver } from './browse-by-dso-breadcrumb.resolver';
|
import { browseByDSOBreadcrumbResolver } from './browse-by-dso-breadcrumb.resolver';
|
||||||
import { BrowseByGuard } from './browse-by-guard';
|
import { browseByGuard } from './browse-by-guard';
|
||||||
import { BrowseByI18nBreadcrumbResolver } from './browse-by-i18n-breadcrumb.resolver';
|
import { browseByI18nBreadcrumbResolver } from './browse-by-i18n-breadcrumb.resolver';
|
||||||
import { BrowseByPageComponent } from './browse-by-page/browse-by-page.component';
|
import { BrowseByPageComponent } from './browse-by-page/browse-by-page.component';
|
||||||
|
|
||||||
export const ROUTES: Route[] = [
|
export const ROUTES: Route[] = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: BrowseByDSOBreadcrumbResolver,
|
breadcrumb: browseByDSOBreadcrumbResolver,
|
||||||
menu: DSOEditMenuResolver,
|
menu: dsoEditMenuResolver,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
component: BrowseByPageComponent,
|
component: BrowseByPageComponent,
|
||||||
canActivate: [BrowseByGuard],
|
canActivate: [browseByGuard],
|
||||||
resolve: { breadcrumb: BrowseByI18nBreadcrumbResolver },
|
resolve: { breadcrumb: browseByI18nBreadcrumbResolver },
|
||||||
data: { title: 'browse.title.page', breadcrumbKey: 'browse.metadata' },
|
data: { title: 'browse.title.page', breadcrumbKey: 'browse.metadata' },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -13,8 +14,9 @@ import { AuthService } from '../core/auth/auth.service';
|
|||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
|
import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
|
||||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { Collection } from '../core/shared/collection.model';
|
import { Collection } from '../core/shared/collection.model';
|
||||||
import { CollectionPageResolver } from './collection-page.resolver';
|
import { collectionPageResolver } from './collection-page.resolver';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -23,11 +25,13 @@ import { CollectionPageResolver } from './collection-page.resolver';
|
|||||||
* Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights
|
* Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights
|
||||||
*/
|
*/
|
||||||
export class CollectionPageAdministratorGuard extends DsoPageSingleFeatureGuard<Collection> {
|
export class CollectionPageAdministratorGuard extends DsoPageSingleFeatureGuard<Collection> {
|
||||||
constructor(protected resolver: CollectionPageResolver,
|
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected resolver: ResolveFn<RemoteData<Collection>> = collectionPageResolver;
|
||||||
|
|
||||||
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService) {
|
protected authService: AuthService) {
|
||||||
super(resolver, authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
import { Route } from '@angular/router';
|
import {
|
||||||
|
mapToCanActivate,
|
||||||
|
Route,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
import { BrowseByGuard } from '../browse-by/browse-by-guard';
|
import { browseByGuard } from '../browse-by/browse-by-guard';
|
||||||
import { BrowseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver';
|
import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver';
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { authenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { CollectionBreadcrumbResolver } from '../core/breadcrumbs/collection-breadcrumb.resolver';
|
import { collectionBreadcrumbResolver } from '../core/breadcrumbs/collection-breadcrumb.resolver';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component';
|
import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component';
|
||||||
import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component';
|
import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component';
|
||||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
import { dsoEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
||||||
import { CollectionPageResolver } from './collection-page.resolver';
|
import { collectionPageResolver } from './collection-page.resolver';
|
||||||
import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard';
|
import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard';
|
||||||
import {
|
import {
|
||||||
COLLECTION_CREATE_PATH,
|
COLLECTION_CREATE_PATH,
|
||||||
@@ -18,9 +21,9 @@ import {
|
|||||||
ITEMTEMPLATE_PATH,
|
ITEMTEMPLATE_PATH,
|
||||||
} from './collection-page-routing-paths';
|
} from './collection-page-routing-paths';
|
||||||
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
||||||
import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
import { createCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
||||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||||
import { ItemTemplatePageResolver } from './edit-item-template-page/item-template-page.resolver';
|
import { itemTemplatePageResolver } from './edit-item-template-page/item-template-page.resolver';
|
||||||
import { ThemedEditItemTemplatePageComponent } from './edit-item-template-page/themed-edit-item-template-page.component';
|
import { ThemedEditItemTemplatePageComponent } from './edit-item-template-page/themed-edit-item-template-page.component';
|
||||||
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
||||||
|
|
||||||
@@ -29,14 +32,14 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: COLLECTION_CREATE_PATH,
|
path: COLLECTION_CREATE_PATH,
|
||||||
component: CreateCollectionPageComponent,
|
component: CreateCollectionPageComponent,
|
||||||
canActivate: [AuthenticatedGuard, CreateCollectionPageGuard],
|
canActivate: [authenticatedGuard, createCollectionPageGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
resolve: {
|
resolve: {
|
||||||
dso: CollectionPageResolver,
|
dso: collectionPageResolver,
|
||||||
breadcrumb: CollectionBreadcrumbResolver,
|
breadcrumb: collectionBreadcrumbResolver,
|
||||||
menu: DSOEditMenuResolver,
|
menu: dsoEditMenuResolver,
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -44,21 +47,21 @@ export const ROUTES: Route[] = [
|
|||||||
path: COLLECTION_EDIT_PATH,
|
path: COLLECTION_EDIT_PATH,
|
||||||
loadChildren: () => import('./edit-collection-page/edit-collection-page-routes')
|
loadChildren: () => import('./edit-collection-page/edit-collection-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [CollectionPageAdministratorGuard],
|
canActivate: mapToCanActivate([CollectionPageAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'delete',
|
path: 'delete',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: DeleteCollectionPageComponent,
|
component: DeleteCollectionPageComponent,
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ITEMTEMPLATE_PATH,
|
path: ITEMTEMPLATE_PATH,
|
||||||
component: ThemedEditItemTemplatePageComponent,
|
component: ThemedEditItemTemplatePageComponent,
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
resolve: {
|
resolve: {
|
||||||
item: ItemTemplatePageResolver,
|
item: itemTemplatePageResolver,
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { title: 'collection.edit.template.title', breadcrumbKey: 'collection.edit.template' },
|
data: { title: 'collection.edit.template.title', breadcrumbKey: 'collection.edit.template' },
|
||||||
},
|
},
|
||||||
@@ -75,9 +78,9 @@ export const ROUTES: Route[] = [
|
|||||||
path: 'browse/:id',
|
path: 'browse/:id',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: ComcolBrowseByComponent,
|
component: ComcolBrowseByComponent,
|
||||||
canActivate: [BrowseByGuard],
|
canActivate: [browseByGuard],
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: BrowseByI18nBreadcrumbResolver,
|
breadcrumb: browseByI18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { breadcrumbKey: 'browse.metadata' },
|
data: { breadcrumbKey: 'browse.metadata' },
|
||||||
},
|
},
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||||
import { CollectionPageResolver } from './collection-page.resolver';
|
import { collectionPageResolver } from './collection-page.resolver';
|
||||||
|
|
||||||
describe('CollectionPageResolver', () => {
|
describe('collectionPageResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: CollectionPageResolver;
|
let resolver: any;
|
||||||
let collectionService: any;
|
let collectionService: any;
|
||||||
let store: any;
|
let store: any;
|
||||||
const uuid = '1234-65487-12354-1235';
|
const uuid = '1234-65487-12354-1235';
|
||||||
@@ -17,12 +18,11 @@ describe('CollectionPageResolver', () => {
|
|||||||
store = jasmine.createSpyObj('store', {
|
store = jasmine.createSpyObj('store', {
|
||||||
dispatch: {},
|
dispatch: {},
|
||||||
});
|
});
|
||||||
resolver = new CollectionPageResolver(collectionService, store);
|
resolver = collectionPageResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve a collection with the correct id', (done) => {
|
it('should resolve a collection with the correct id', (done) => {
|
||||||
resolver.resolve({ params: { id: uuid } } as any, { url: 'current-url' } as any)
|
(resolver({ params: { id: uuid } } as any, { url: 'current-url' } as any, collectionService, store) as Observable<any>).pipe(first())
|
||||||
.pipe(first())
|
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(resolved) => {
|
(resolved) => {
|
||||||
expect(resolved.payload.id).toEqual(uuid);
|
expect(resolved.payload.id).toEqual(uuid);
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { AppState } from '../app.reducer';
|
||||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { ResolvedAction } from '../core/resolving/resolver.actions';
|
import { ResolvedAction } from '../core/resolving/resolver.actions';
|
||||||
@@ -29,25 +30,21 @@ export const COLLECTION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Collection>[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific collection before the route is activated
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
|
|
||||||
constructor(
|
|
||||||
private collectionService: CollectionDataService,
|
|
||||||
private store: Store<any>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a collection based on the parameters in the current route
|
* Method for resolving a collection based on the parameters in the current route
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param collectionService
|
||||||
|
* @param store
|
||||||
* @returns Observable<<RemoteData<Collection>> Emits the found collection based on the parameters in the current route,
|
* @returns Observable<<RemoteData<Collection>> Emits the found collection based on the parameters in the current route,
|
||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
|
export const collectionPageResolver: ResolveFn<RemoteData<Collection>> = (
|
||||||
const collectionRD$ = this.collectionService.findById(
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
collectionService: CollectionDataService = inject(CollectionDataService),
|
||||||
|
store: Store<AppState> = inject(Store<AppState>),
|
||||||
|
): Observable<RemoteData<Collection>> => {
|
||||||
|
const collectionRD$ = collectionService.findById(
|
||||||
route.params.id,
|
route.params.id,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@@ -57,9 +54,8 @@ export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
collectionRD$.subscribe((collectionRD: RemoteData<Collection>) => {
|
collectionRD$.subscribe((collectionRD: RemoteData<Collection>) => {
|
||||||
this.store.dispatch(new ResolvedAction(state.url, collectionRD.payload));
|
store.dispatch(new ResolvedAction(state.url, collectionRD.payload));
|
||||||
});
|
});
|
||||||
|
|
||||||
return collectionRD$;
|
return collectionRD$;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -6,11 +6,11 @@ import {
|
|||||||
createFailedRemoteDataObject$,
|
createFailedRemoteDataObject$,
|
||||||
createSuccessfulRemoteDataObject$,
|
createSuccessfulRemoteDataObject$,
|
||||||
} from '../../shared/remote-data.utils';
|
} from '../../shared/remote-data.utils';
|
||||||
import { CreateCollectionPageGuard } from './create-collection-page.guard';
|
import { createCollectionPageGuard } from './create-collection-page.guard';
|
||||||
|
|
||||||
describe('CreateCollectionPageGuard', () => {
|
describe('createCollectionPageGuard', () => {
|
||||||
describe('canActivate', () => {
|
describe('canActivate', () => {
|
||||||
let guard: CreateCollectionPageGuard;
|
let guard: any;
|
||||||
let router;
|
let router;
|
||||||
let communityDataServiceStub: any;
|
let communityDataServiceStub: any;
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ describe('CreateCollectionPageGuard', () => {
|
|||||||
};
|
};
|
||||||
router = new RouterMock();
|
router = new RouterMock();
|
||||||
|
|
||||||
guard = new CreateCollectionPageGuard(router, communityDataServiceStub);
|
guard = createCollectionPageGuard;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when the parent ID resolves to a community', () => {
|
it('should return true when the parent ID resolves to a community', () => {
|
||||||
guard.canActivate({ queryParams: { parent: 'valid-id' } } as any, undefined)
|
guard({ queryParams: { parent: 'valid-id' } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
@@ -41,7 +41,7 @@ describe('CreateCollectionPageGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when no parent ID has been provided', () => {
|
it('should return false when no parent ID has been provided', () => {
|
||||||
guard.canActivate({ queryParams: { } } as any, undefined)
|
guard({ queryParams: { } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
@@ -50,7 +50,7 @@ describe('CreateCollectionPageGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when the parent ID does not resolve to a community', () => {
|
it('should return false when the parent ID does not resolve to a community', () => {
|
||||||
guard.canActivate({ queryParams: { parent: 'invalid-id' } } as any, undefined)
|
guard({ queryParams: { parent: 'invalid-id' } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
@@ -59,7 +59,7 @@ describe('CreateCollectionPageGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when the parent ID resolves to an error response', () => {
|
it('should return false when the parent ID resolves to an error response', () => {
|
||||||
guard.canActivate({ queryParams: { parent: 'error-id' } } as any, undefined)
|
guard({ queryParams: { parent: 'error-id' } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
CanActivateFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -24,34 +24,29 @@ import {
|
|||||||
} from '../../shared/empty.util';
|
} from '../../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent creation of a collection without a parent community provided
|
|
||||||
* @class CreateCollectionPageGuard
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class CreateCollectionPageGuard implements CanActivate {
|
|
||||||
public constructor(private router: Router, private communityService: CommunityDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True when either a parent ID query parameter has been provided and the parent ID resolves to a valid parent community
|
* True when either a parent ID query parameter has been provided and the parent ID resolves to a valid parent community
|
||||||
* Reroutes to a 404 page when the page cannot be activated
|
* Reroutes to a 404 page when the page cannot be activated
|
||||||
* @method canActivate
|
|
||||||
*/
|
*/
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
export const createCollectionPageGuard: CanActivateFn = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
communityService: CommunityDataService = inject(CommunityDataService),
|
||||||
|
router: Router = inject(Router),
|
||||||
|
): Observable<boolean> => {
|
||||||
const parentID = route.queryParams.parent;
|
const parentID = route.queryParams.parent;
|
||||||
if (hasNoValue(parentID)) {
|
if (hasNoValue(parentID)) {
|
||||||
this.router.navigate(['/404']);
|
router.navigate(['/404']);
|
||||||
return observableOf(false);
|
return observableOf(false);
|
||||||
}
|
}
|
||||||
return this.communityService.findById(parentID)
|
return communityService.findById(parentID)
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((communityRD: RemoteData<Community>) => hasValue(communityRD) && communityRD.hasSucceeded && hasValue(communityRD.payload)),
|
map((communityRD: RemoteData<Community>) => hasValue(communityRD) && communityRD.hasSucceeded && hasValue(communityRD.payload)),
|
||||||
tap((isValid: boolean) => {
|
tap((isValid: boolean) => {
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
this.router.navigate(['/404']);
|
router.navigate(['/404']);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
import { Route } from '@angular/router';
|
import {
|
||||||
|
mapToCanActivate,
|
||||||
|
Route,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { CollectionAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard';
|
import { CollectionAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard';
|
||||||
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
|
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
|
||||||
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
|
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
|
||||||
import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
|
import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
|
||||||
import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver';
|
import { resourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver';
|
||||||
import { CollectionItemMapperComponent } from '../collection-item-mapper/collection-item-mapper.component';
|
import { CollectionItemMapperComponent } from '../collection-item-mapper/collection-item-mapper.component';
|
||||||
import { CollectionAccessControlComponent } from './collection-access-control/collection-access-control.component';
|
import { CollectionAccessControlComponent } from './collection-access-control/collection-access-control.component';
|
||||||
import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component';
|
import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component';
|
||||||
@@ -23,11 +26,11 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { breadcrumbKey: 'collection.edit' },
|
data: { breadcrumbKey: 'collection.edit' },
|
||||||
component: EditCollectionPageComponent,
|
component: EditCollectionPageComponent,
|
||||||
canActivate: [CollectionAdministratorGuard],
|
canActivate: mapToCanActivate([CollectionAdministratorGuard]),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@@ -70,7 +73,7 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
resolve: {
|
resolve: {
|
||||||
resourcePolicyTarget: ResourcePolicyTargetResolver,
|
resourcePolicyTarget: resourcePolicyTargetResolver,
|
||||||
},
|
},
|
||||||
component: ResourcePolicyCreateComponent,
|
component: ResourcePolicyCreateComponent,
|
||||||
data: { title: 'resource-policies.create.page.title' },
|
data: { title: 'resource-policies.create.page.title' },
|
||||||
@@ -78,7 +81,7 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'edit',
|
path: 'edit',
|
||||||
resolve: {
|
resolve: {
|
||||||
resourcePolicy: ResourcePolicyResolver,
|
resourcePolicy: resourcePolicyResolver,
|
||||||
},
|
},
|
||||||
component: ResourcePolicyEditComponent,
|
component: ResourcePolicyEditComponent,
|
||||||
data: { title: 'resource-policies.edit.page.title' },
|
data: { title: 'resource-policies.edit.page.title' },
|
||||||
|
@@ -1,27 +1,24 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
|
||||||
import { DSONameServiceMock } from '../../shared/mocks/dso-name.service.mock';
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { ItemTemplatePageResolver } from './item-template-page.resolver';
|
import { itemTemplatePageResolver } from './item-template-page.resolver';
|
||||||
|
|
||||||
describe('ItemTemplatePageResolver', () => {
|
describe('itemTemplatePageResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: ItemTemplatePageResolver;
|
let resolver: any;
|
||||||
let itemTemplateService: any;
|
let itemTemplateService: any;
|
||||||
let dsoNameService: DSONameServiceMock;
|
|
||||||
const uuid = '1234-65487-12354-1235';
|
const uuid = '1234-65487-12354-1235';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
itemTemplateService = {
|
itemTemplateService = {
|
||||||
findByCollectionID: (id: string) => createSuccessfulRemoteDataObject$({ id }),
|
findByCollectionID: (id: string) => createSuccessfulRemoteDataObject$({ id }),
|
||||||
};
|
};
|
||||||
dsoNameService = new DSONameServiceMock();
|
resolver = itemTemplatePageResolver;
|
||||||
resolver = new ItemTemplatePageResolver(dsoNameService as DSONameService, itemTemplateService);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve an item template with the correct id', (done) => {
|
it('should resolve an item template with the correct id', (done) => {
|
||||||
resolver.resolve({ params: { id: uuid } } as any, undefined)
|
(resolver({ params: { id: uuid } } as any, undefined, itemTemplateService) as Observable<any>)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(resolved) => {
|
(resolved) => {
|
||||||
|
@@ -1,39 +1,23 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
|
||||||
import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
|
import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
/**
|
export const itemTemplatePageResolver: ResolveFn<RemoteData<Item>> = (
|
||||||
* This class represents a resolver that requests a specific collection's item template before the route is activated
|
route: ActivatedRouteSnapshot,
|
||||||
*/
|
state: RouterStateSnapshot,
|
||||||
@Injectable({ providedIn: 'root' })
|
itemTemplateService: ItemTemplateDataService = inject(ItemTemplateDataService),
|
||||||
export class ItemTemplatePageResolver implements Resolve<RemoteData<Item>> {
|
): Observable<RemoteData<Item>> => {
|
||||||
constructor(
|
return itemTemplateService.findByCollectionID(route.params.id, true, false, followLink('templateItemOf')).pipe(
|
||||||
public dsoNameService: DSONameService,
|
|
||||||
private itemTemplateService: ItemTemplateDataService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a collection's item template based on the parameters in the current route
|
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
|
||||||
* @returns Observable<<RemoteData<Collection>> Emits the found item template based on the parameters in the current route,
|
|
||||||
* or an error if something went wrong
|
|
||||||
*/
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
|
|
||||||
return this.itemTemplateService.findByCollectionID(route.params.id, true, false, followLink('templateItemOf')).pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Route } from '@angular/router';
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { ThemedCommunityListPageComponent } from './themed-community-list-page.component';
|
import { ThemedCommunityListPageComponent } from './themed-community-list-page.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,7 +12,7 @@ export const ROUTES: Route[] = [
|
|||||||
component: ThemedCommunityListPageComponent,
|
component: ThemedCommunityListPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { title: 'communityList.tabTitle', breadcrumbKey: 'communityList' },
|
data: { title: 'communityList.tabTitle', breadcrumbKey: 'communityList' },
|
||||||
},
|
},
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle>
|
<span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle>
|
||||||
<span class="fa fa-chevron-right"></span>
|
<span class="fa fa-chevron-right"></span>
|
||||||
</span>
|
</span>
|
||||||
<div class="align-middle pt-2">
|
<div class="align-middle my-auto">
|
||||||
<button *ngIf="(dataSource.loading$ | async) !== true" (click)="getNextPage(node)"
|
<button *ngIf="(dataSource.loading$ | async) !== true" (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 }}
|
||||||
@@ -37,10 +37,10 @@
|
|||||||
<span class="fa fa-chevron-right"></span>
|
<span class="fa fa-chevron-right"></span>
|
||||||
</span>
|
</span>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<span class="align-middle pt-2 lead">
|
<span class="d-flex align-middle my-auto">
|
||||||
<a [routerLink]="node.route" class="lead">{{ dsoNameService.getName(node.payload) }}</a>
|
<a [routerLink]="node.route" class="lead">{{ dsoNameService.getName(node.payload) }}</a>
|
||||||
<span class="pr-2"> </span>
|
<span class="pr-2"> </span>
|
||||||
<span *ngIf="node.payload.archivedItemsCount >= 0" class="badge badge-pill badge-secondary align-top archived-items-lead">{{node.payload.archivedItemsCount}}</span>
|
<span *ngIf="node.payload.archivedItemsCount >= 0" class="badge badge-pill badge-secondary align-top archived-items-lead my-auto">{{node.payload.archivedItemsCount}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
<span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle>
|
<span aria-hidden="true" class="btn btn-default invisible" cdkTreeNodeToggle>
|
||||||
<span class="fa fa-chevron-right"></span>
|
<span class="fa fa-chevron-right"></span>
|
||||||
</span>
|
</span>
|
||||||
<h6 class="align-middle pt-2">
|
<h6 class="align-middle my-auto">
|
||||||
<a [routerLink]="node.route" class="lead">{{ dsoNameService.getName(node.payload) }}</a>
|
<a [routerLink]="node.route" class="lead">{{ dsoNameService.getName(node.payload) }}</a>
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -13,8 +14,9 @@ import { AuthService } from '../core/auth/auth.service';
|
|||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
|
import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
|
||||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { Community } from '../core/shared/community.model';
|
import { Community } from '../core/shared/community.model';
|
||||||
import { CommunityPageResolver } from './community-page.resolver';
|
import { communityPageResolver } from './community-page.resolver';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -23,11 +25,13 @@ import { CommunityPageResolver } from './community-page.resolver';
|
|||||||
* Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights
|
* Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights
|
||||||
*/
|
*/
|
||||||
export class CommunityPageAdministratorGuard extends DsoPageSingleFeatureGuard<Community> {
|
export class CommunityPageAdministratorGuard extends DsoPageSingleFeatureGuard<Community> {
|
||||||
constructor(protected resolver: CommunityPageResolver,
|
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected resolver: ResolveFn<RemoteData<Community>> = communityPageResolver;
|
||||||
|
|
||||||
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService) {
|
protected authService: AuthService) {
|
||||||
super(resolver, authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,23 +1,26 @@
|
|||||||
import { Route } from '@angular/router';
|
import {
|
||||||
|
mapToCanActivate,
|
||||||
|
Route,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
import { BrowseByGuard } from '../browse-by/browse-by-guard';
|
import { browseByGuard } from '../browse-by/browse-by-guard';
|
||||||
import { BrowseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver';
|
import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver';
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { authenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
|
import { communityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component';
|
import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component';
|
||||||
import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component';
|
import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component';
|
||||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
import { dsoEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
||||||
import { CommunityPageResolver } from './community-page.resolver';
|
import { communityPageResolver } from './community-page.resolver';
|
||||||
import { CommunityPageAdministratorGuard } from './community-page-administrator.guard';
|
import { CommunityPageAdministratorGuard } from './community-page-administrator.guard';
|
||||||
import {
|
import {
|
||||||
COMMUNITY_CREATE_PATH,
|
COMMUNITY_CREATE_PATH,
|
||||||
COMMUNITY_EDIT_PATH,
|
COMMUNITY_EDIT_PATH,
|
||||||
} from './community-page-routing-paths';
|
} from './community-page-routing-paths';
|
||||||
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
||||||
import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
import { createCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
||||||
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
||||||
import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-com-col-section.component';
|
import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-com-col-section.component';
|
||||||
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
||||||
@@ -26,14 +29,14 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: COMMUNITY_CREATE_PATH,
|
path: COMMUNITY_CREATE_PATH,
|
||||||
component: CreateCommunityPageComponent,
|
component: CreateCommunityPageComponent,
|
||||||
canActivate: [AuthenticatedGuard, CreateCommunityPageGuard],
|
canActivate: [authenticatedGuard, createCommunityPageGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
resolve: {
|
resolve: {
|
||||||
dso: CommunityPageResolver,
|
dso: communityPageResolver,
|
||||||
breadcrumb: CommunityBreadcrumbResolver,
|
breadcrumb: communityBreadcrumbResolver,
|
||||||
menu: DSOEditMenuResolver,
|
menu: dsoEditMenuResolver,
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -41,13 +44,13 @@ export const ROUTES: Route[] = [
|
|||||||
path: COMMUNITY_EDIT_PATH,
|
path: COMMUNITY_EDIT_PATH,
|
||||||
loadChildren: () => import('./edit-community-page/edit-community-page-routes')
|
loadChildren: () => import('./edit-community-page/edit-community-page-routes')
|
||||||
.then((m) => m.ROUTES),
|
.then((m) => m.ROUTES),
|
||||||
canActivate: [CommunityPageAdministratorGuard],
|
canActivate: mapToCanActivate([CommunityPageAdministratorGuard]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'delete',
|
path: 'delete',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: DeleteCommunityPageComponent,
|
component: DeleteCommunityPageComponent,
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [authenticatedGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@@ -63,7 +66,7 @@ export const ROUTES: Route[] = [
|
|||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: SubComColSectionComponent,
|
component: SubComColSectionComponent,
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { breadcrumbKey: 'community.subcoms-cols' },
|
data: { breadcrumbKey: 'community.subcoms-cols' },
|
||||||
},
|
},
|
||||||
@@ -71,9 +74,9 @@ export const ROUTES: Route[] = [
|
|||||||
path: 'browse/:id',
|
path: 'browse/:id',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: ComcolBrowseByComponent,
|
component: ComcolBrowseByComponent,
|
||||||
canActivate: [BrowseByGuard],
|
canActivate: [browseByGuard],
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: BrowseByI18nBreadcrumbResolver,
|
breadcrumb: browseByI18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { breadcrumbKey: 'browse.metadata' },
|
data: { breadcrumbKey: 'browse.metadata' },
|
||||||
},
|
},
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||||
import { CommunityPageResolver } from './community-page.resolver';
|
import { communityPageResolver } from './community-page.resolver';
|
||||||
|
|
||||||
describe('CommunityPageResolver', () => {
|
describe('communityPageResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: CommunityPageResolver;
|
let resolver: any;
|
||||||
let communityService: any;
|
let communityService: any;
|
||||||
let store: any;
|
let store: any;
|
||||||
const uuid = '1234-65487-12354-1235';
|
const uuid = '1234-65487-12354-1235';
|
||||||
@@ -17,11 +18,11 @@ describe('CommunityPageResolver', () => {
|
|||||||
store = jasmine.createSpyObj('store', {
|
store = jasmine.createSpyObj('store', {
|
||||||
dispatch: {},
|
dispatch: {},
|
||||||
});
|
});
|
||||||
resolver = new CommunityPageResolver(communityService, store);
|
resolver = communityPageResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve a community with the correct id', (done) => {
|
it('should resolve a community with the correct id', (done) => {
|
||||||
resolver.resolve({ params: { id: uuid } } as any, { url: 'current-url' } as any)
|
(resolver({ params: { id: uuid } } as any, { url: 'current-url' } as any, communityService, store) as Observable<any>)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(resolved) => {
|
(resolved) => {
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { AppState } from '../app.reducer';
|
||||||
import { CommunityDataService } from '../core/data/community-data.service';
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { ResolvedAction } from '../core/resolving/resolver.actions';
|
import { ResolvedAction } from '../core/resolving/resolver.actions';
|
||||||
@@ -29,25 +30,21 @@ export const COMMUNITY_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Community>[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific community before the route is activated
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
|
|
||||||
constructor(
|
|
||||||
private communityService: CommunityDataService,
|
|
||||||
private store: Store<any>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a community based on the parameters in the current route
|
* Method for resolving a community based on the parameters in the current route
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {CommunityDataService} communityService
|
||||||
|
* @param {Store} store
|
||||||
* @returns Observable<<RemoteData<Community>> Emits the found community based on the parameters in the current route,
|
* @returns Observable<<RemoteData<Community>> Emits the found community based on the parameters in the current route,
|
||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
|
export const communityPageResolver: ResolveFn<RemoteData<Community>> = (
|
||||||
const communityRD$ = this.communityService.findById(
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
communityService: CommunityDataService = inject(CommunityDataService),
|
||||||
|
store: Store<AppState> = inject(Store<AppState>),
|
||||||
|
): Observable<RemoteData<Community>> => {
|
||||||
|
const communityRD$ = communityService.findById(
|
||||||
route.params.id,
|
route.params.id,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@@ -57,9 +54,8 @@ export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
communityRD$.subscribe((communityRD: RemoteData<Community>) => {
|
communityRD$.subscribe((communityRD: RemoteData<Community>) => {
|
||||||
this.store.dispatch(new ResolvedAction(state.url, communityRD.payload));
|
store.dispatch(new ResolvedAction(state.url, communityRD.payload));
|
||||||
});
|
});
|
||||||
|
|
||||||
return communityRD$;
|
return communityRD$;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -6,11 +6,11 @@ import {
|
|||||||
createFailedRemoteDataObject$,
|
createFailedRemoteDataObject$,
|
||||||
createSuccessfulRemoteDataObject$,
|
createSuccessfulRemoteDataObject$,
|
||||||
} from '../../shared/remote-data.utils';
|
} from '../../shared/remote-data.utils';
|
||||||
import { CreateCommunityPageGuard } from './create-community-page.guard';
|
import { createCommunityPageGuard } from './create-community-page.guard';
|
||||||
|
|
||||||
describe('CreateCommunityPageGuard', () => {
|
describe('createCommunityPageGuard', () => {
|
||||||
describe('canActivate', () => {
|
describe('canActivate', () => {
|
||||||
let guard: CreateCommunityPageGuard;
|
let guard: any;
|
||||||
let router;
|
let router;
|
||||||
let communityDataServiceStub: any;
|
let communityDataServiceStub: any;
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ describe('CreateCommunityPageGuard', () => {
|
|||||||
};
|
};
|
||||||
router = new RouterMock();
|
router = new RouterMock();
|
||||||
|
|
||||||
guard = new CreateCommunityPageGuard(router, communityDataServiceStub);
|
guard = createCommunityPageGuard;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when the parent ID resolves to a community', () => {
|
it('should return true when the parent ID resolves to a community', () => {
|
||||||
guard.canActivate({ queryParams: { parent: 'valid-id' } } as any, undefined)
|
guard({ queryParams: { parent: 'valid-id' } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
@@ -41,7 +41,7 @@ describe('CreateCommunityPageGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when no parent ID has been provided', () => {
|
it('should return true when no parent ID has been provided', () => {
|
||||||
guard.canActivate({ queryParams: { } } as any, undefined)
|
guard({ queryParams: { } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
@@ -50,7 +50,7 @@ describe('CreateCommunityPageGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when the parent ID does not resolve to a community', () => {
|
it('should return false when the parent ID does not resolve to a community', () => {
|
||||||
guard.canActivate({ queryParams: { parent: 'invalid-id' } } as any, undefined)
|
guard({ queryParams: { parent: 'invalid-id' } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
@@ -59,7 +59,7 @@ describe('CreateCommunityPageGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when the parent ID resolves to an error response', () => {
|
it('should return false when the parent ID resolves to an error response', () => {
|
||||||
guard.canActivate({ queryParams: { parent: 'error-id' } } as any, undefined)
|
guard({ queryParams: { parent: 'error-id' } } as any, undefined, communityDataServiceStub, router)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(canActivate) =>
|
(canActivate) =>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
CanActivateFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -24,35 +24,29 @@ import {
|
|||||||
} from '../../shared/empty.util';
|
} from '../../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent creation of a community with an invalid parent community provided
|
|
||||||
* @class CreateCommunityPageGuard
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class CreateCommunityPageGuard implements CanActivate {
|
|
||||||
public constructor(private router: Router, private communityService: CommunityDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True when either NO parent ID query parameter has been provided, or the parent ID resolves to a valid parent community
|
* True when either NO parent ID query parameter has been provided, or the parent ID resolves to a valid parent community
|
||||||
* Reroutes to a 404 page when the page cannot be activated
|
* Reroutes to a 404 page when the page cannot be activated
|
||||||
* @method canActivate
|
|
||||||
*/
|
*/
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
export const createCommunityPageGuard: CanActivateFn = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
communityService: CommunityDataService = inject(CommunityDataService),
|
||||||
|
router: Router = inject(Router),
|
||||||
|
): Observable<boolean> => {
|
||||||
const parentID = route.queryParams.parent;
|
const parentID = route.queryParams.parent;
|
||||||
if (hasNoValue(parentID)) {
|
if (hasNoValue(parentID)) {
|
||||||
return observableOf(true);
|
return observableOf(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.communityService.findById(parentID)
|
return communityService.findById(parentID)
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((communityRD: RemoteData<Community>) => hasValue(communityRD) && communityRD.hasSucceeded && hasValue(communityRD.payload)),
|
map((communityRD: RemoteData<Community>) => hasValue(communityRD) && communityRD.hasSucceeded && hasValue(communityRD.payload)),
|
||||||
tap((isValid: boolean) => {
|
tap((isValid: boolean) => {
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
this.router.navigate(['/404']);
|
router.navigate(['/404']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
import { Route } from '@angular/router';
|
import {
|
||||||
|
mapToCanActivate,
|
||||||
|
Route,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
import { CommunityAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/community-administrator.guard';
|
import { CommunityAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/community-administrator.guard';
|
||||||
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
|
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
|
||||||
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
|
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
|
||||||
import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
|
import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
|
||||||
import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver';
|
import { resourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver';
|
||||||
import { CommunityAccessControlComponent } from './community-access-control/community-access-control.component';
|
import { CommunityAccessControlComponent } from './community-access-control/community-access-control.component';
|
||||||
import { CommunityAuthorizationsComponent } from './community-authorizations/community-authorizations.component';
|
import { CommunityAuthorizationsComponent } from './community-authorizations/community-authorizations.component';
|
||||||
import { CommunityCurateComponent } from './community-curate/community-curate.component';
|
import { CommunityCurateComponent } from './community-curate/community-curate.component';
|
||||||
@@ -21,11 +24,11 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
resolve: {
|
resolve: {
|
||||||
breadcrumb: I18nBreadcrumbResolver,
|
breadcrumb: i18nBreadcrumbResolver,
|
||||||
},
|
},
|
||||||
data: { breadcrumbKey: 'community.edit' },
|
data: { breadcrumbKey: 'community.edit' },
|
||||||
component: EditCommunityPageComponent,
|
component: EditCommunityPageComponent,
|
||||||
canActivate: [CommunityAdministratorGuard],
|
canActivate: mapToCanActivate([CommunityAdministratorGuard]),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@@ -63,7 +66,7 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
resolve: {
|
resolve: {
|
||||||
resourcePolicyTarget: ResourcePolicyTargetResolver,
|
resourcePolicyTarget: resourcePolicyTargetResolver,
|
||||||
},
|
},
|
||||||
component: ResourcePolicyCreateComponent,
|
component: ResourcePolicyCreateComponent,
|
||||||
data: { title: 'resource-policies.create.page.title' },
|
data: { title: 'resource-policies.create.page.title' },
|
||||||
@@ -71,7 +74,7 @@ export const ROUTES: Route[] = [
|
|||||||
{
|
{
|
||||||
path: 'edit',
|
path: 'edit',
|
||||||
resolve: {
|
resolve: {
|
||||||
resourcePolicy: ResourcePolicyResolver,
|
resourcePolicy: resourcePolicyResolver,
|
||||||
},
|
},
|
||||||
component: ResourcePolicyEditComponent,
|
component: ResourcePolicyEditComponent,
|
||||||
data: { title: 'resource-policies.edit.page.title' },
|
data: { title: 'resource-policies.edit.page.title' },
|
||||||
|
@@ -17,10 +17,10 @@ import {
|
|||||||
storeModuleConfig,
|
storeModuleConfig,
|
||||||
} from '../../app.reducer';
|
} from '../../app.reducer';
|
||||||
import { authReducer } from './auth.reducer';
|
import { authReducer } from './auth.reducer';
|
||||||
import { AuthBlockingGuard } from './auth-blocking.guard';
|
import { authBlockingGuard } from './auth-blocking.guard';
|
||||||
|
|
||||||
describe('AuthBlockingGuard', () => {
|
describe('authBlockingGuard', () => {
|
||||||
let guard: AuthBlockingGuard;
|
let guard: any;
|
||||||
let initialState;
|
let initialState;
|
||||||
let store: Store<AppState>;
|
let store: Store<AppState>;
|
||||||
let mockStore: MockStore<AppState>;
|
let mockStore: MockStore<AppState>;
|
||||||
@@ -44,7 +44,7 @@ describe('AuthBlockingGuard', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
provideMockStore({ initialState }),
|
provideMockStore({ initialState }),
|
||||||
{ provide: AuthBlockingGuard, useValue: guard },
|
{ provide: authBlockingGuard, useValue: guard },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
@@ -52,14 +52,14 @@ describe('AuthBlockingGuard', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
store = TestBed.inject(Store);
|
store = TestBed.inject(Store);
|
||||||
mockStore = store as MockStore<AppState>;
|
mockStore = store as MockStore<AppState>;
|
||||||
guard = new AuthBlockingGuard(store);
|
guard = authBlockingGuard;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`canActivate`, () => {
|
describe(`canActivate`, () => {
|
||||||
|
|
||||||
describe(`when authState.blocking is undefined`, () => {
|
describe(`when authState.blocking is undefined`, () => {
|
||||||
it(`should not emit anything`, (done) => {
|
it(`should not emit anything`, (done) => {
|
||||||
expect(guard.canActivate()).toBeObservable(cold('-'));
|
expect(guard(null, null, store)).toBeObservable(cold('-'));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -77,7 +77,7 @@ describe('AuthBlockingGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it(`should not emit anything`, (done) => {
|
it(`should not emit anything`, (done) => {
|
||||||
expect(guard.canActivate()).toBeObservable(cold('-'));
|
expect(guard(null, null, store)).toBeObservable(cold('-'));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -95,7 +95,7 @@ describe('AuthBlockingGuard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it(`should succeed`, (done) => {
|
it(`should succeed`, (done) => {
|
||||||
expect(guard.canActivate()).toBeObservable(cold('(a|)', { a: true }));
|
expect(guard(null, null, store)).toBeObservable(cold('(a|)', { a: true }));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { CanActivate } from '@angular/router';
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
CanActivateFn,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
} from '@angular/router';
|
||||||
import {
|
import {
|
||||||
select,
|
select,
|
||||||
Store,
|
Store,
|
||||||
@@ -20,24 +24,16 @@ import { isAuthenticationBlocking } from './selectors';
|
|||||||
* route until the authentication status has loaded.
|
* route until the authentication status has loaded.
|
||||||
* To ensure all rest requests get the correct auth header.
|
* To ensure all rest requests get the correct auth header.
|
||||||
*/
|
*/
|
||||||
@Injectable({
|
export const authBlockingGuard: CanActivateFn = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class AuthBlockingGuard implements CanActivate {
|
store: Store<AppState> = inject(Store<AppState>),
|
||||||
|
): Observable<boolean> => {
|
||||||
constructor(private store: Store<AppState>) {
|
return store.pipe(select(isAuthenticationBlocking)).pipe(
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True when the authentication isn't blocking everything
|
|
||||||
*/
|
|
||||||
canActivate(): Observable<boolean> {
|
|
||||||
return this.store.pipe(select(isAuthenticationBlocking)).pipe(
|
|
||||||
map((isBlocking: boolean) => isBlocking === false),
|
map((isBlocking: boolean) => isBlocking === false),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
filter((finished: boolean) => finished === true),
|
filter((finished: boolean) => finished === true),
|
||||||
take(1),
|
take(1),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
CanActivateChildFn,
|
||||||
|
CanActivateFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
UrlTree,
|
UrlTree,
|
||||||
@@ -17,7 +18,7 @@ import {
|
|||||||
switchMap,
|
switchMap,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { CoreState } from '../core-state.model';
|
import { AppState } from '../../app.reducer';
|
||||||
import {
|
import {
|
||||||
AuthService,
|
AuthService,
|
||||||
LOGIN_ROUTE,
|
LOGIN_ROUTE,
|
||||||
@@ -29,49 +30,35 @@ import {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent unauthorized activating and loading of routes
|
* Prevent unauthorized activating and loading of routes
|
||||||
* @class AuthenticatedGuard
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class AuthenticatedGuard implements CanActivate {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor(private authService: AuthService, private router: Router, private store: Store<CoreState>) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True when user is authenticated
|
* True when user is authenticated
|
||||||
* UrlTree with redirect to login page when user isn't authenticated
|
* UrlTree with redirect to login page when user isn't authenticated
|
||||||
* @method canActivate
|
* @method canActivate
|
||||||
*/
|
*/
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
|
export const authenticatedGuard: CanActivateFn = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
authService: AuthService = inject(AuthService),
|
||||||
|
router: Router = inject(Router),
|
||||||
|
store: Store<AppState> = inject(Store<AppState>),
|
||||||
|
): Observable<boolean | UrlTree> => {
|
||||||
const url = state.url;
|
const url = state.url;
|
||||||
return this.handleAuth(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True when user is authenticated
|
|
||||||
* UrlTree with redirect to login page when user isn't authenticated
|
|
||||||
* @method canActivateChild
|
|
||||||
*/
|
|
||||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
|
|
||||||
return this.canActivate(route, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleAuth(url: string): Observable<boolean | UrlTree> {
|
|
||||||
// redirect to sign in page if user is not authenticated
|
// redirect to sign in page if user is not authenticated
|
||||||
return this.store.pipe(select(isAuthenticationLoading)).pipe(
|
return store.pipe(select(isAuthenticationLoading)).pipe(
|
||||||
find((isLoading: boolean) => isLoading === false),
|
find((isLoading: boolean) => isLoading === false),
|
||||||
switchMap(() => this.store.pipe(select(isAuthenticated))),
|
switchMap(() => store.pipe(select(isAuthenticated))),
|
||||||
map((authenticated) => {
|
map((authenticated) => {
|
||||||
if (authenticated) {
|
if (authenticated) {
|
||||||
return authenticated;
|
return authenticated;
|
||||||
} else {
|
} else {
|
||||||
this.authService.setRedirectUrl(url);
|
authService.setRedirectUrl(url);
|
||||||
this.authService.removeToken();
|
authService.removeToken();
|
||||||
return this.router.createUrlTree([LOGIN_ROUTE]);
|
return router.createUrlTree([LOGIN_ROUTE]);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
export const AuthenticatedGuardChild: CanActivateChildFn = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
) => authenticatedGuard(route, state);
|
||||||
|
@@ -1,31 +1,36 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { BITSTREAM_PAGE_LINKS_TO_FOLLOW } from '../../bitstream-page/bitstream-page.resolver';
|
import { BITSTREAM_PAGE_LINKS_TO_FOLLOW } from '../../bitstream-page/bitstream-page.resolver';
|
||||||
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { BitstreamDataService } from '../data/bitstream-data.service';
|
import { BitstreamDataService } from '../data/bitstream-data.service';
|
||||||
import { Bitstream } from '../shared/bitstream.model';
|
import { Bitstream } from '../shared/bitstream.model';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { BitstreamBreadcrumbsService } from './bitstream-breadcrumbs.service';
|
import { BitstreamBreadcrumbsService } from './bitstream-breadcrumbs.service';
|
||||||
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for an Item
|
* The resolve function that resolves the BreadcrumbConfig object for an Item
|
||||||
*/
|
*/
|
||||||
@Injectable({
|
export const bitstreamBreadcrumbResolver: ResolveFn<BreadcrumbConfig<Bitstream>> = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class BitstreamBreadcrumbResolver extends DSOBreadcrumbResolver<Bitstream> {
|
breadcrumbService: BitstreamBreadcrumbsService = inject(BitstreamBreadcrumbsService),
|
||||||
constructor(
|
dataService: BitstreamDataService = inject(BitstreamDataService),
|
||||||
protected breadcrumbService: BitstreamBreadcrumbsService, protected dataService: BitstreamDataService) {
|
): Observable<BreadcrumbConfig<Bitstream>> => {
|
||||||
super(breadcrumbService, dataService);
|
const linksToFollow: FollowLinkConfig<DSpaceObject>[] = BITSTREAM_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig<DSpaceObject>[];
|
||||||
}
|
return DSOBreadcrumbResolver(
|
||||||
|
route,
|
||||||
|
state,
|
||||||
|
breadcrumbService,
|
||||||
|
dataService,
|
||||||
|
...linksToFollow,
|
||||||
|
) as Observable<BreadcrumbConfig<Bitstream>>;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that returns the follow links to already resolve
|
|
||||||
* The self links defined in this list are expected to be requested somewhere in the near future
|
|
||||||
* Requesting them as embeds will limit the number of requests
|
|
||||||
*/
|
|
||||||
get followLinks(): FollowLinkConfig<Bitstream>[] {
|
|
||||||
return BITSTREAM_PAGE_LINKS_TO_FOLLOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -1,29 +1,36 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { COLLECTION_PAGE_LINKS_TO_FOLLOW } from '../../collection-page/collection-page.resolver';
|
import { COLLECTION_PAGE_LINKS_TO_FOLLOW } from '../../collection-page/collection-page.resolver';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { CollectionDataService } from '../data/collection-data.service';
|
import { CollectionDataService } from '../data/collection-data.service';
|
||||||
import { Collection } from '../shared/collection.model';
|
import { Collection } from '../shared/collection.model';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
||||||
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for a Collection
|
* The resolve function that resolves the BreadcrumbConfig object for a Collection
|
||||||
*/
|
*/
|
||||||
@Injectable({
|
export const collectionBreadcrumbResolver: ResolveFn<BreadcrumbConfig<Collection>> = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class CollectionBreadcrumbResolver extends DSOBreadcrumbResolver<Collection> {
|
breadcrumbService: DSOBreadcrumbsService = inject(DSOBreadcrumbsService),
|
||||||
constructor(protected breadcrumbService: DSOBreadcrumbsService, protected dataService: CollectionDataService) {
|
dataService: CollectionDataService = inject(CollectionDataService),
|
||||||
super(breadcrumbService, dataService);
|
): Observable<BreadcrumbConfig<Collection>> => {
|
||||||
}
|
const linksToFollow: FollowLinkConfig<DSpaceObject>[] = COLLECTION_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig<DSpaceObject>[];
|
||||||
|
return DSOBreadcrumbResolver(
|
||||||
|
route,
|
||||||
|
state,
|
||||||
|
breadcrumbService,
|
||||||
|
dataService,
|
||||||
|
...linksToFollow,
|
||||||
|
) as Observable<BreadcrumbConfig<Collection>>;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that returns the follow links to already resolve
|
|
||||||
* The self links defined in this list are expected to be requested somewhere in the near future
|
|
||||||
* Requesting them as embeds will limit the number of requests
|
|
||||||
*/
|
|
||||||
get followLinks(): FollowLinkConfig<Collection>[] {
|
|
||||||
return COLLECTION_PAGE_LINKS_TO_FOLLOW;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,29 +1,35 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { COMMUNITY_PAGE_LINKS_TO_FOLLOW } from '../../community-page/community-page.resolver';
|
import { COMMUNITY_PAGE_LINKS_TO_FOLLOW } from '../../community-page/community-page.resolver';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { CommunityDataService } from '../data/community-data.service';
|
import { CommunityDataService } from '../data/community-data.service';
|
||||||
import { Community } from '../shared/community.model';
|
import { Community } from '../shared/community.model';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
||||||
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for a Community
|
* The resolve function that resolves the BreadcrumbConfig object for a Community
|
||||||
*/
|
*/
|
||||||
@Injectable({
|
export const communityBreadcrumbResolver: ResolveFn<BreadcrumbConfig<Community>> = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class CommunityBreadcrumbResolver extends DSOBreadcrumbResolver<Community> {
|
breadcrumbService: DSOBreadcrumbsService = inject(DSOBreadcrumbsService),
|
||||||
constructor(protected breadcrumbService: DSOBreadcrumbsService, protected dataService: CommunityDataService) {
|
dataService: CommunityDataService = inject(CommunityDataService),
|
||||||
super(breadcrumbService, dataService);
|
): Observable<BreadcrumbConfig<Community>> => {
|
||||||
}
|
const linksToFollow: FollowLinkConfig<DSpaceObject>[] = COMMUNITY_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig<DSpaceObject>[];
|
||||||
|
return DSOBreadcrumbResolver(
|
||||||
/**
|
route,
|
||||||
* Method that returns the follow links to already resolve
|
state,
|
||||||
* The self links defined in this list are expected to be requested somewhere in the near future
|
breadcrumbService,
|
||||||
* Requesting them as embeds will limit the number of requests
|
dataService,
|
||||||
*/
|
...linksToFollow,
|
||||||
get followLinks(): FollowLinkConfig<Community>[] {
|
) as Observable<BreadcrumbConfig<Community>>;
|
||||||
return COMMUNITY_PAGE_LINKS_TO_FOLLOW;
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -2,12 +2,11 @@ import { getTestScheduler } from 'jasmine-marbles';
|
|||||||
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { Collection } from '../shared/collection.model';
|
import { Collection } from '../shared/collection.model';
|
||||||
import { CollectionBreadcrumbResolver } from './collection-breadcrumb.resolver';
|
import { collectionBreadcrumbResolver } from './collection-breadcrumb.resolver';
|
||||||
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
|
||||||
|
|
||||||
describe('DSOBreadcrumbResolver', () => {
|
describe('DSOBreadcrumbResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: DSOBreadcrumbResolver<Collection>;
|
let resolver: any;
|
||||||
let collectionService: any;
|
let collectionService: any;
|
||||||
let dsoBreadcrumbService: any;
|
let dsoBreadcrumbService: any;
|
||||||
let testCollection: Collection;
|
let testCollection: Collection;
|
||||||
@@ -17,18 +16,18 @@ describe('DSOBreadcrumbResolver', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
uuid = '1234-65487-12354-1235';
|
uuid = '1234-65487-12354-1235';
|
||||||
breadcrumbUrl = '/collections/' + uuid;
|
breadcrumbUrl = `/collections/${uuid}`;
|
||||||
currentUrl = breadcrumbUrl + '/edit';
|
currentUrl = `${breadcrumbUrl}/edit`;
|
||||||
testCollection = Object.assign(new Collection(), { uuid });
|
testCollection = Object.assign(new Collection(), { uuid });
|
||||||
dsoBreadcrumbService = {};
|
dsoBreadcrumbService = {};
|
||||||
collectionService = {
|
collectionService = {
|
||||||
findById: (id: string) => createSuccessfulRemoteDataObject$(testCollection),
|
findById: () => createSuccessfulRemoteDataObject$(testCollection),
|
||||||
};
|
};
|
||||||
resolver = new CollectionBreadcrumbResolver(dsoBreadcrumbService, collectionService);
|
resolver = collectionBreadcrumbResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve a breadcrumb config for the correct DSO', () => {
|
it('should resolve a breadcrumb config for the correct DSO', () => {
|
||||||
const resolvedConfig = resolver.resolve({ params: { id: uuid } } as any, { url: currentUrl } as any);
|
const resolvedConfig = resolver({ params: { id: uuid } } as any, { url: currentUrl } as any, dsoBreadcrumbService, collectionService);
|
||||||
const expectedConfig = { provider: dsoBreadcrumbService, key: testCollection, url: breadcrumbUrl };
|
const expectedConfig = { provider: dsoBreadcrumbService, key: testCollection, url: breadcrumbUrl };
|
||||||
getTestScheduler().expectObservable(resolvedConfig).toBe('(a|)', { a: expectedConfig });
|
getTestScheduler().expectObservable(resolvedConfig).toBe('(a|)', { a: expectedConfig });
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -11,7 +9,6 @@ import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config
|
|||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
|
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
|
||||||
import { ChildHALResource } from '../shared/child-hal-resource.model';
|
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import {
|
import {
|
||||||
getFirstCompletedRemoteData,
|
getFirstCompletedRemoteData,
|
||||||
@@ -20,45 +17,33 @@ import {
|
|||||||
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for a DSpaceObject
|
|
||||||
*/
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export abstract class DSOBreadcrumbResolver<T extends ChildHALResource & DSpaceObject> implements Resolve<BreadcrumbConfig<T>> {
|
|
||||||
protected constructor(
|
|
||||||
protected breadcrumbService: DSOBreadcrumbsService,
|
|
||||||
protected dataService: IdentifiableDataService<T>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving a breadcrumb config object
|
* Method for resolving a breadcrumb config object
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {DSOBreadcrumbsService} breadcrumbService
|
||||||
|
* @param {IdentifiableDataService} dataService
|
||||||
|
* @param linksToFollow
|
||||||
* @returns BreadcrumbConfig object
|
* @returns BreadcrumbConfig object
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<T>> {
|
export const DSOBreadcrumbResolver: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot, breadcrumbService: DSOBreadcrumbsService, dataService: IdentifiableDataService<DSpaceObject>, ...linksToFollow: FollowLinkConfig<DSpaceObject>[]) => Observable<BreadcrumbConfig<DSpaceObject>> = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
breadcrumbService: DSOBreadcrumbsService,
|
||||||
|
dataService: IdentifiableDataService<DSpaceObject>,
|
||||||
|
...linksToFollow: FollowLinkConfig<DSpaceObject>[]
|
||||||
|
): Observable<BreadcrumbConfig<DSpaceObject>> => {
|
||||||
const uuid = route.params.id;
|
const uuid = route.params.id;
|
||||||
return this.dataService.findById(uuid, true, false, ...this.followLinks).pipe(
|
return dataService.findById(uuid, true, false, ...linksToFollow).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
map((object: T) => {
|
map((object: DSpaceObject) => {
|
||||||
if (hasValue(object)) {
|
if (hasValue(object)) {
|
||||||
const fullPath = state.url;
|
const fullPath = state.url;
|
||||||
const url = fullPath.substr(0, fullPath.indexOf(uuid)) + uuid;
|
const url = (fullPath.substring(0, fullPath.indexOf(uuid))).concat(uuid);
|
||||||
return { provider: this.breadcrumbService, key: object, url: url };
|
return { provider: breadcrumbService, key: object, url: url };
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that returns the follow links to already resolve
|
|
||||||
* The self links defined in this list are expected to be requested somewhere in the near future
|
|
||||||
* Requesting them as embeds will limit the number of requests
|
|
||||||
*/
|
|
||||||
abstract get followLinks(): FollowLinkConfig<T>[];
|
|
||||||
}
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
import { I18nBreadcrumbResolver } from './i18n-breadcrumb.resolver';
|
import { i18nBreadcrumbResolver } from './i18n-breadcrumb.resolver';
|
||||||
|
|
||||||
describe('I18nBreadcrumbResolver', () => {
|
describe('i18nBreadcrumbResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: I18nBreadcrumbResolver;
|
let resolver: any;
|
||||||
let i18nBreadcrumbService: any;
|
let i18nBreadcrumbService: any;
|
||||||
let i18nKey: string;
|
let i18nKey: string;
|
||||||
let route: any;
|
let route: any;
|
||||||
@@ -27,18 +27,18 @@ describe('I18nBreadcrumbResolver', () => {
|
|||||||
};
|
};
|
||||||
expectedPath = new URLCombiner(parentSegment, segment).toString();
|
expectedPath = new URLCombiner(parentSegment, segment).toString();
|
||||||
i18nBreadcrumbService = {};
|
i18nBreadcrumbService = {};
|
||||||
resolver = new I18nBreadcrumbResolver(i18nBreadcrumbService);
|
resolver = i18nBreadcrumbResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve the breadcrumb config', () => {
|
it('should resolve the breadcrumb config', () => {
|
||||||
const resolvedConfig = resolver.resolve(route, {} as any);
|
const resolvedConfig = resolver(route, {} as any, i18nBreadcrumbService);
|
||||||
const expectedConfig = { provider: i18nBreadcrumbService, key: i18nKey, url: expectedPath };
|
const expectedConfig = { provider: i18nBreadcrumbService, key: i18nKey, url: expectedPath };
|
||||||
expect(resolvedConfig).toEqual(expectedConfig);
|
expect(resolvedConfig).toEqual(expectedConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve throw an error when no breadcrumbKey is defined', () => {
|
it('should resolve throw an error when no breadcrumbKey is defined', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
resolver.resolve({ data: {} } as any, undefined);
|
resolver({ data: {} } as any, undefined, i18nBreadcrumbService);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
@@ -11,27 +11,21 @@ import { currentPathFromSnapshot } from '../../shared/utils/route.utils';
|
|||||||
import { I18nBreadcrumbsService } from './i18n-breadcrumbs.service';
|
import { I18nBreadcrumbsService } from './i18n-breadcrumbs.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves a BreadcrumbConfig object with an i18n key string for a route
|
|
||||||
*/
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class I18nBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
|
|
||||||
constructor(protected breadcrumbService: I18nBreadcrumbsService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for resolving an I18n breadcrumb configuration object
|
* Method for resolving an I18n breadcrumb configuration object
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {I18nBreadcrumbsService} breadcrumbService
|
||||||
* @returns BreadcrumbConfig object
|
* @returns BreadcrumbConfig object
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
export const i18nBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
breadcrumbService: I18nBreadcrumbsService = inject(I18nBreadcrumbsService),
|
||||||
|
): BreadcrumbConfig<string> => {
|
||||||
const key = route.data.breadcrumbKey;
|
const key = route.data.breadcrumbKey;
|
||||||
if (hasNoValue(key)) {
|
if (hasNoValue(key)) {
|
||||||
throw new Error('You provided an i18nBreadcrumbResolver for url \"' + route.url + '\" but no breadcrumbKey in the route\'s data');
|
throw new Error('You provided an i18nBreadcrumbResolver for url \"' + route.url + '\" but no breadcrumbKey in the route\'s data');
|
||||||
}
|
}
|
||||||
const fullPath = currentPathFromSnapshot(route);
|
const fullPath = currentPathFromSnapshot(route);
|
||||||
return { provider: this.breadcrumbService, key: key, url: fullPath };
|
return { provider: breadcrumbService, key: key, url: fullPath };
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,29 +1,35 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
ResolveFn,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item-page/item.resolver';
|
import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item-page/item.resolver';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { ItemDataService } from '../data/item-data.service';
|
import { ItemDataService } from '../data/item-data.service';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver';
|
||||||
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for an Item
|
* The resolve function that resolves the BreadcrumbConfig object for an Item
|
||||||
*/
|
*/
|
||||||
@Injectable({
|
export const itemBreadcrumbResolver: ResolveFn<BreadcrumbConfig<Item>> = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class ItemBreadcrumbResolver extends DSOBreadcrumbResolver<Item> {
|
breadcrumbService: DSOBreadcrumbsService = inject(DSOBreadcrumbsService),
|
||||||
constructor(protected breadcrumbService: DSOBreadcrumbsService, protected dataService: ItemDataService) {
|
dataService: ItemDataService = inject(ItemDataService),
|
||||||
super(breadcrumbService, dataService);
|
): Observable<BreadcrumbConfig<Item>> => {
|
||||||
}
|
const linksToFollow: FollowLinkConfig<DSpaceObject>[] = ITEM_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig<DSpaceObject>[];
|
||||||
|
return DSOBreadcrumbResolver(
|
||||||
/**
|
route,
|
||||||
* Method that returns the follow links to already resolve
|
state,
|
||||||
* The self links defined in this list are expected to be requested somewhere in the near future
|
breadcrumbService,
|
||||||
* Requesting them as embeds will limit the number of requests
|
dataService,
|
||||||
*/
|
...linksToFollow,
|
||||||
get followLinks(): FollowLinkConfig<Item>[] {
|
) as Observable<BreadcrumbConfig<Item>>;
|
||||||
return ITEM_PAGE_LINKS_TO_FOLLOW;
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { NavigationBreadcrumbResolver } from './navigation-breadcrumb.resolver';
|
import { navigationBreadcrumbResolver } from './navigation-breadcrumb.resolver';
|
||||||
|
|
||||||
describe('NavigationBreadcrumbResolver', () => {
|
describe('navigationBreadcrumbResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: NavigationBreadcrumbResolver;
|
let resolver: any;
|
||||||
let NavigationBreadcrumbService: any;
|
let NavigationBreadcrumbService: any;
|
||||||
let i18nKey: string;
|
let i18nKey: string;
|
||||||
let relatedI18nKey: string;
|
let relatedI18nKey: string;
|
||||||
@@ -40,11 +40,11 @@ describe('NavigationBreadcrumbResolver', () => {
|
|||||||
};
|
};
|
||||||
expectedPath = '/base/example:/base';
|
expectedPath = '/base/example:/base';
|
||||||
NavigationBreadcrumbService = {};
|
NavigationBreadcrumbService = {};
|
||||||
resolver = new NavigationBreadcrumbResolver(NavigationBreadcrumbService);
|
resolver = navigationBreadcrumbResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve the breadcrumb config', () => {
|
it('should resolve the breadcrumb config', () => {
|
||||||
const resolvedConfig = resolver.resolve(route, state);
|
const resolvedConfig = resolver(route, state, NavigationBreadcrumbService);
|
||||||
const expectedConfig = { provider: NavigationBreadcrumbService, key: `${i18nKey}:${relatedI18nKey}`, url: expectedPath };
|
const expectedConfig = { provider: NavigationBreadcrumbService, key: `${i18nKey}:${relatedI18nKey}`, url: expectedPath };
|
||||||
expect(resolvedConfig).toEqual(expectedConfig);
|
expect(resolvedConfig).toEqual(expectedConfig);
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
@@ -9,37 +9,21 @@ import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config
|
|||||||
import { NavigationBreadcrumbsService } from './navigation-breadcrumb.service';
|
import { NavigationBreadcrumbsService } from './navigation-breadcrumb.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves a BreadcrumbConfig object with an i18n key string for a route and related parents
|
|
||||||
*/
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class NavigationBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
|
|
||||||
|
|
||||||
private parentRoutes: ActivatedRouteSnapshot[] = [];
|
|
||||||
constructor(protected breadcrumbService: NavigationBreadcrumbsService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to collect all parent routes snapshot from current route snapshot
|
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
|
||||||
*/
|
|
||||||
private getParentRoutes(route: ActivatedRouteSnapshot): void {
|
|
||||||
if (route.parent) {
|
|
||||||
this.parentRoutes.push(route.parent);
|
|
||||||
this.getParentRoutes(route.parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method for resolving an I18n breadcrumb configuration object
|
* Method for resolving an I18n breadcrumb configuration object
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @param {NavigationBreadcrumbsService} breadcrumbService
|
||||||
* @returns BreadcrumbConfig object
|
* @returns BreadcrumbConfig object
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
export const navigationBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
||||||
this.getParentRoutes(route);
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
breadcrumbService: NavigationBreadcrumbsService = inject(NavigationBreadcrumbsService),
|
||||||
|
): BreadcrumbConfig<string> => {
|
||||||
|
const parentRoutes: ActivatedRouteSnapshot[] = [];
|
||||||
|
getParentRoutes(route, parentRoutes);
|
||||||
const relatedRoutes = route.data.relatedRoutes;
|
const relatedRoutes = route.data.relatedRoutes;
|
||||||
const parentPaths = this.parentRoutes.map(parent => parent.routeConfig?.path);
|
const parentPaths = parentRoutes.map(parent => parent.routeConfig?.path);
|
||||||
const relatedParentRoutes = relatedRoutes.filter(relatedRoute => parentPaths.includes(relatedRoute.path));
|
const relatedParentRoutes = relatedRoutes.filter(relatedRoute => parentPaths.includes(relatedRoute.path));
|
||||||
const baseUrlSegmentPath = route.parent.url[route.parent.url.length - 1].path;
|
const baseUrlSegmentPath = route.parent.url[route.parent.url.length - 1].path;
|
||||||
const baseUrl = state.url.substring(0, state.url.lastIndexOf(baseUrlSegmentPath) + baseUrlSegmentPath.length);
|
const baseUrl = state.url.substring(0, state.url.lastIndexOf(baseUrlSegmentPath) + baseUrlSegmentPath.length);
|
||||||
@@ -52,6 +36,17 @@ export class NavigationBreadcrumbResolver implements Resolve<BreadcrumbConfig<st
|
|||||||
return `${previous}:${baseUrl}${current.path}`;
|
return `${previous}:${baseUrl}${current.path}`;
|
||||||
}, state.url);
|
}, state.url);
|
||||||
|
|
||||||
return { provider: this.breadcrumbService, key: combinedParentBreadcrumbKeys, url: combinedUrls };
|
return { provider: breadcrumbService, key: combinedParentBreadcrumbKeys, url: combinedUrls };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to collect all parent routes snapshot from current route snapshot
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {ActivatedRouteSnapshot[]} parentRoutes
|
||||||
|
*/
|
||||||
|
function getParentRoutes(route: ActivatedRouteSnapshot, parentRoutes: ActivatedRouteSnapshot[]): void {
|
||||||
|
if (route.parent) {
|
||||||
|
parentRoutes.push(route.parent);
|
||||||
|
getParentRoutes(route.parent, parentRoutes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { PublicationClaimBreadcrumbResolver } from './publication-claim-breadcrumb.resolver';
|
import { publicationClaimBreadcrumbResolver } from './publication-claim-breadcrumb.resolver';
|
||||||
|
|
||||||
describe('PublicationClaimBreadcrumbResolver', () => {
|
describe('publicationClaimBreadcrumbResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: PublicationClaimBreadcrumbResolver;
|
let resolver: any;
|
||||||
let publicationClaimBreadcrumbService: any;
|
let publicationClaimBreadcrumbService: any;
|
||||||
const fullPath = '/test/publication-claim/openaire:6bee076d-4f2a-4555-a475-04a267769b2a';
|
const fullPath = '/test/publication-claim/openaire:6bee076d-4f2a-4555-a475-04a267769b2a';
|
||||||
const expectedKey = '6bee076d-4f2a-4555-a475-04a267769b2a';
|
const expectedKey = '6bee076d-4f2a-4555-a475-04a267769b2a';
|
||||||
@@ -19,11 +19,11 @@ describe('PublicationClaimBreadcrumbResolver', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
publicationClaimBreadcrumbService = {};
|
publicationClaimBreadcrumbService = {};
|
||||||
resolver = new PublicationClaimBreadcrumbResolver(publicationClaimBreadcrumbService);
|
resolver = publicationClaimBreadcrumbResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve the breadcrumb config', () => {
|
it('should resolve the breadcrumb config', () => {
|
||||||
const resolvedConfig = resolver.resolve(route as any, { url: fullPath } as any);
|
const resolvedConfig = resolver(route as any, { url: fullPath } as any, publicationClaimBreadcrumbService);
|
||||||
const expectedConfig = { provider: publicationClaimBreadcrumbService, key: expectedKey };
|
const expectedConfig = { provider: publicationClaimBreadcrumbService, key: expectedKey };
|
||||||
expect(resolvedConfig).toEqual(expectedConfig);
|
expect(resolvedConfig).toEqual(expectedConfig);
|
||||||
});
|
});
|
||||||
|
@@ -1,29 +1,18 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { PublicationClaimBreadcrumbService } from './publication-claim-breadcrumb.service';
|
import { PublicationClaimBreadcrumbService } from './publication-claim-breadcrumb.service';
|
||||||
|
|
||||||
@Injectable({
|
export const publicationClaimBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class PublicationClaimBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
|
breadcrumbService: PublicationClaimBreadcrumbService = inject(PublicationClaimBreadcrumbService),
|
||||||
constructor(protected breadcrumbService: PublicationClaimBreadcrumbService) {
|
): BreadcrumbConfig<string> => {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that resolve Publication Claim item into a breadcrumb
|
|
||||||
* The parameter are retrieved by the url since part of the Publication Claim route config
|
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
|
||||||
* @returns BreadcrumbConfig object
|
|
||||||
*/
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
|
||||||
const targetId = route.paramMap.get('targetId').split(':')[1];
|
const targetId = route.paramMap.get('targetId').split(':')[1];
|
||||||
return { provider: this.breadcrumbService, key: targetId };
|
return { provider: breadcrumbService, key: targetId };
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { QualityAssuranceBreadcrumbResolver } from './quality-assurance-breadcrumb.resolver';
|
import { qualityAssuranceBreadcrumbResolver } from './quality-assurance-breadcrumb.resolver';
|
||||||
|
|
||||||
describe('QualityAssuranceBreadcrumbResolver', () => {
|
describe('qualityAssuranceBreadcrumbResolver', () => {
|
||||||
describe('resolve', () => {
|
describe('resolve', () => {
|
||||||
let resolver: QualityAssuranceBreadcrumbResolver;
|
let resolver: any;
|
||||||
let qualityAssuranceBreadcrumbService: any;
|
let qualityAssuranceBreadcrumbService: any;
|
||||||
let route: any;
|
let route: any;
|
||||||
const fullPath = '/test/quality-assurance/';
|
const fullPath = '/test/quality-assurance/';
|
||||||
@@ -19,11 +19,11 @@ describe('QualityAssuranceBreadcrumbResolver', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
qualityAssuranceBreadcrumbService = {};
|
qualityAssuranceBreadcrumbService = {};
|
||||||
resolver = new QualityAssuranceBreadcrumbResolver(qualityAssuranceBreadcrumbService);
|
resolver = qualityAssuranceBreadcrumbResolver;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve the breadcrumb config', () => {
|
it('should resolve the breadcrumb config', () => {
|
||||||
const resolvedConfig = resolver.resolve(route as any, { url: fullPath + 'testSourceId' } as any);
|
const resolvedConfig = resolver(route as any, { url: fullPath + 'testSourceId' } as any, qualityAssuranceBreadcrumbService);
|
||||||
const expectedConfig = { provider: qualityAssuranceBreadcrumbService, key: expectedKey, url: fullPath };
|
const expectedConfig = { provider: qualityAssuranceBreadcrumbService, key: expectedKey, url: fullPath };
|
||||||
expect(resolvedConfig).toEqual(expectedConfig);
|
expect(resolvedConfig).toEqual(expectedConfig);
|
||||||
});
|
});
|
||||||
|
@@ -1,27 +1,18 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
import { QualityAssuranceBreadcrumbService } from './quality-assurance-breadcrumb.service';
|
import { QualityAssuranceBreadcrumbService } from './quality-assurance-breadcrumb.service';
|
||||||
|
|
||||||
@Injectable({
|
export const qualityAssuranceBreadcrumbResolver: ResolveFn<BreadcrumbConfig<string>> = (
|
||||||
providedIn: 'root',
|
route: ActivatedRouteSnapshot,
|
||||||
})
|
state: RouterStateSnapshot,
|
||||||
export class QualityAssuranceBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
|
breadcrumbService: QualityAssuranceBreadcrumbService = inject(QualityAssuranceBreadcrumbService),
|
||||||
constructor(protected breadcrumbService: QualityAssuranceBreadcrumbService) {}
|
): BreadcrumbConfig<string> => {
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that resolve QA item into a breadcrumb
|
|
||||||
* The parameter are retrieved by the url since part of the QA route config
|
|
||||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
|
||||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
|
||||||
* @returns BreadcrumbConfig object
|
|
||||||
*/
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
|
||||||
const sourceId = route.paramMap.get('sourceId');
|
const sourceId = route.paramMap.get('sourceId');
|
||||||
const topicId = route.paramMap.get('topicId');
|
const topicId = route.paramMap.get('topicId');
|
||||||
let key = sourceId;
|
let key = sourceId;
|
||||||
@@ -30,8 +21,7 @@ export class QualityAssuranceBreadcrumbResolver implements Resolve<BreadcrumbCon
|
|||||||
key += `:${topicId}`;
|
key += `:${topicId}`;
|
||||||
}
|
}
|
||||||
const fullPath = state.url;
|
const fullPath = state.url;
|
||||||
const url = fullPath.substr(0, fullPath.indexOf(sourceId));
|
const url = fullPath.substring(0, fullPath.indexOf(sourceId));
|
||||||
|
|
||||||
return { provider: this.breadcrumbService, key, url };
|
return { provider: breadcrumbService, key, url };
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -37,9 +37,9 @@ class TestModel implements HALResource {
|
|||||||
successor?: TestModel;
|
successor?: TestModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockDataServiceMap: any = {
|
const mockDataServiceMap: any = new Map([
|
||||||
[TEST_MODEL.value]: () => import('../../../shared/testing/test-data-service.mock').then(m => m.TestDataService),
|
[TEST_MODEL.value, () => import('../../../shared/testing/test-data-service.mock').then(m => m.TestDataService)],
|
||||||
};
|
]);
|
||||||
|
|
||||||
let testDataService: TestDataService;
|
let testDataService: TestDataService;
|
||||||
|
|
||||||
|
7
src/app/core/cache/builders/link.service.ts
vendored
7
src/app/core/cache/builders/link.service.ts
vendored
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Inject,
|
Inject,
|
||||||
Injectable,
|
Injectable,
|
||||||
InjectionToken,
|
|
||||||
Injector,
|
Injector,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
@@ -25,7 +24,7 @@ import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model
|
|||||||
import { HALDataService } from '../../data/base/hal-data-service.interface';
|
import { HALDataService } from '../../data/base/hal-data-service.interface';
|
||||||
import { PaginatedList } from '../../data/paginated-list.model';
|
import { PaginatedList } from '../../data/paginated-list.model';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
import { lazyService } from '../../lazy-service';
|
import { lazyDataService } from '../../lazy-data-service';
|
||||||
import { GenericConstructor } from '../../shared/generic-constructor';
|
import { GenericConstructor } from '../../shared/generic-constructor';
|
||||||
import { HALResource } from '../../shared/hal-resource.model';
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
import {
|
import {
|
||||||
@@ -43,7 +42,7 @@ export class LinkService {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected injector: Injector,
|
protected injector: Injector,
|
||||||
@Inject(APP_DATA_SERVICES_MAP) private map: InjectionToken<LazyDataServicesMap>,
|
@Inject(APP_DATA_SERVICES_MAP) private map: LazyDataServicesMap,
|
||||||
@Inject(LINK_DEFINITION_FACTORY) private getLinkDefinition: <T extends HALResource>(source: GenericConstructor<T>, linkName: keyof T['_links']) => LinkDefinition<T>,
|
@Inject(LINK_DEFINITION_FACTORY) private getLinkDefinition: <T extends HALResource>(source: GenericConstructor<T>, linkName: keyof T['_links']) => LinkDefinition<T>,
|
||||||
@Inject(LINK_DEFINITION_MAP_FACTORY) private getLinkDefinitions: <T extends HALResource>(source: GenericConstructor<T>) => Map<keyof T['_links'], LinkDefinition<T>>,
|
@Inject(LINK_DEFINITION_MAP_FACTORY) private getLinkDefinitions: <T extends HALResource>(source: GenericConstructor<T>) => Map<keyof T['_links'], LinkDefinition<T>>,
|
||||||
) {
|
) {
|
||||||
@@ -73,7 +72,7 @@ export class LinkService {
|
|||||||
public resolveLinkWithoutAttaching<T extends HALResource, U extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): Observable<RemoteData<U | PaginatedList<U>>> {
|
public resolveLinkWithoutAttaching<T extends HALResource, U extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): Observable<RemoteData<U | PaginatedList<U>>> {
|
||||||
const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name);
|
const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name);
|
||||||
if (hasValue(matchingLinkDef)) {
|
if (hasValue(matchingLinkDef)) {
|
||||||
const lazyProvider$: Observable<HALDataService<any>> = lazyService(this.map[matchingLinkDef.resourceType.value], this.injector);
|
const lazyProvider$: Observable<HALDataService<any>> = lazyDataService(this.map, matchingLinkDef.resourceType.value, this.injector);
|
||||||
return lazyProvider$.pipe(
|
return lazyProvider$.pipe(
|
||||||
switchMap((provider: HALDataService<any>) => {
|
switchMap((provider: HALDataService<any>) => {
|
||||||
const link = model._links[matchingLinkDef.linkName];
|
const link = model._links[matchingLinkDef.linkName];
|
||||||
|
@@ -1,36 +1,27 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
import { NotifyInfoGuard } from './notify-info.guard';
|
import { notifyInfoGuard } from './notify-info.guard';
|
||||||
import { NotifyInfoService } from './notify-info.service';
|
|
||||||
|
|
||||||
describe('NotifyInfoGuard', () => {
|
describe('notifyInfoGuard', () => {
|
||||||
let guard: NotifyInfoGuard;
|
let guard: any;
|
||||||
let notifyInfoServiceSpy: any;
|
let notifyInfoServiceSpy: any;
|
||||||
let router: any;
|
let router: any;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
notifyInfoServiceSpy = jasmine.createSpyObj('NotifyInfoService', ['isCoarConfigEnabled']);
|
notifyInfoServiceSpy = jasmine.createSpyObj('NotifyInfoService', ['isCoarConfigEnabled']);
|
||||||
router = jasmine.createSpyObj('Router', ['parseUrl']);
|
router = jasmine.createSpyObj('Router', ['parseUrl']);
|
||||||
TestBed.configureTestingModule({
|
guard = notifyInfoGuard;
|
||||||
providers: [
|
|
||||||
NotifyInfoGuard,
|
|
||||||
{ provide: NotifyInfoService, useValue: notifyInfoServiceSpy },
|
|
||||||
{ provide: Router, useValue: router },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
guard = TestBed.inject(NotifyInfoGuard);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(guard).toBeTruthy();
|
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(true));
|
||||||
|
expect(guard(null, null, notifyInfoServiceSpy, router)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if COAR config is enabled', (done) => {
|
it('should return true if COAR config is enabled', (done) => {
|
||||||
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(true));
|
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(true));
|
||||||
|
|
||||||
guard.canActivate(null, null).subscribe((result) => {
|
guard(null, null, notifyInfoServiceSpy, router).subscribe((result) => {
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -40,7 +31,7 @@ describe('NotifyInfoGuard', () => {
|
|||||||
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(false));
|
notifyInfoServiceSpy.isCoarConfigEnabled.and.returnValue(of(false));
|
||||||
router.parseUrl.and.returnValue(of('/404'));
|
router.parseUrl.and.returnValue(of('/404'));
|
||||||
|
|
||||||
guard.canActivate(null, null).subscribe(() => {
|
guard(null, null, notifyInfoServiceSpy, router).subscribe(() => {
|
||||||
expect(router.parseUrl).toHaveBeenCalledWith('/404');
|
expect(router.parseUrl).toHaveBeenCalledWith('/404');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
CanActivateFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
UrlTree,
|
UrlTree,
|
||||||
@@ -11,27 +11,13 @@ import { map } from 'rxjs/operators';
|
|||||||
|
|
||||||
import { NotifyInfoService } from './notify-info.service';
|
import { NotifyInfoService } from './notify-info.service';
|
||||||
|
|
||||||
@Injectable({
|
export const notifyInfoGuard: CanActivateFn = (
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class NotifyInfoGuard implements CanActivate {
|
|
||||||
constructor(
|
|
||||||
private notifyInfoService: NotifyInfoService,
|
|
||||||
private router: Router,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
canActivate(
|
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot,
|
state: RouterStateSnapshot,
|
||||||
): Observable<boolean | UrlTree> {
|
notifyInfoService: NotifyInfoService = inject(NotifyInfoService),
|
||||||
return this.notifyInfoService.isCoarConfigEnabled().pipe(
|
router: Router = inject(Router),
|
||||||
map(coarLdnEnabled => {
|
): Observable<boolean | UrlTree> => {
|
||||||
if (coarLdnEnabled) {
|
return notifyInfoService.isCoarConfigEnabled().pipe(
|
||||||
return true;
|
map(isEnabled => isEnabled ? true : router.parseUrl('/404')),
|
||||||
} else {
|
|
||||||
return this.router.parseUrl('/404');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@@ -69,73 +69,71 @@ import { CLAIMED_TASK } from './tasks/models/claimed-task-object.resource-type';
|
|||||||
import { POOL_TASK } from './tasks/models/pool-task-object.resource-type';
|
import { POOL_TASK } from './tasks/models/pool-task-object.resource-type';
|
||||||
import { WORKFLOW_ACTION } from './tasks/models/workflow-action-object.resource-type';
|
import { WORKFLOW_ACTION } from './tasks/models/workflow-action-object.resource-type';
|
||||||
|
|
||||||
export const LAZY_DATA_SERVICES: LazyDataServicesMap = {
|
export const LAZY_DATA_SERVICES: LazyDataServicesMap = new Map([
|
||||||
[AUTHORIZATION.value]: () => import('./data/feature-authorization/authorization-data.service').then(m => m.AuthorizationDataService),
|
[AUTHORIZATION.value, () => import('./data/feature-authorization/authorization-data.service').then(m => m.AuthorizationDataService)],
|
||||||
[BROWSE_DEFINITION.value]: () => import('./browse/browse-definition-data.service').then(m => m.BrowseDefinitionDataService),
|
[BROWSE_DEFINITION.value, () => import('./browse/browse-definition-data.service').then(m => m.BrowseDefinitionDataService)],
|
||||||
[BULK_ACCESS_CONDITION_OPTIONS.value]: () => import('./config/bulk-access-config-data.service').then(m => m.BulkAccessConfigDataService),
|
[BULK_ACCESS_CONDITION_OPTIONS.value, () => import('./config/bulk-access-config-data.service').then(m => m.BulkAccessConfigDataService)],
|
||||||
[METADATA_SCHEMA.value]: () => import('./data/metadata-schema-data.service').then(m => m.MetadataSchemaDataService),
|
[METADATA_SCHEMA.value, () => import('./data/metadata-schema-data.service').then(m => m.MetadataSchemaDataService)],
|
||||||
[SUBMISSION_UPLOADS_TYPE.value]: () => import('./config/submission-uploads-config-data.service').then(m => m.SubmissionUploadsConfigDataService),
|
[SUBMISSION_UPLOADS_TYPE.value, () => import('./config/submission-uploads-config-data.service').then(m => m.SubmissionUploadsConfigDataService)],
|
||||||
[BITSTREAM.value]: () => import('./data/bitstream-data.service').then(m => m.BitstreamDataService),
|
[BITSTREAM.value, () => import('./data/bitstream-data.service').then(m => m.BitstreamDataService)],
|
||||||
[SUBMISSION_ACCESSES_TYPE.value]: () => import('./config/submission-accesses-config-data.service').then(m => m.SubmissionAccessesConfigDataService),
|
[SUBMISSION_ACCESSES_TYPE.value, () => import('./config/submission-accesses-config-data.service').then(m => m.SubmissionAccessesConfigDataService)],
|
||||||
[SYSTEMWIDEALERT.value]: () => import('./data/system-wide-alert-data.service').then(m => m.SystemWideAlertDataService),
|
[SYSTEMWIDEALERT.value, () => import('./data/system-wide-alert-data.service').then(m => m.SystemWideAlertDataService)],
|
||||||
[USAGE_REPORT.value]: () => import('./statistics/usage-report-data.service').then(m => m.UsageReportDataService),
|
[USAGE_REPORT.value, () => import('./statistics/usage-report-data.service').then(m => m.UsageReportDataService)],
|
||||||
[ACCESS_STATUS.value]: () => import('./data/access-status-data.service').then(m => m.AccessStatusDataService),
|
[ACCESS_STATUS.value, () => import('./data/access-status-data.service').then(m => m.AccessStatusDataService)],
|
||||||
[COLLECTION.value]: () => import('./data/collection-data.service').then(m => m.CollectionDataService),
|
[COLLECTION.value, () => import('./data/collection-data.service').then(m => m.CollectionDataService)],
|
||||||
[CLAIMED_TASK.value]: () => import('./tasks/claimed-task-data.service').then(m => m.ClaimedTaskDataService),
|
[CLAIMED_TASK.value, () => import('./tasks/claimed-task-data.service').then(m => m.ClaimedTaskDataService)],
|
||||||
[VOCABULARY_ENTRY.value]: () => import('./data/href-only-data.service').then(m => m.HrefOnlyDataService),
|
[VOCABULARY_ENTRY.value, () => import('./data/href-only-data.service').then(m => m.HrefOnlyDataService)],
|
||||||
[ITEM_TYPE.value]: () => import('./data/href-only-data.service').then(m => m.HrefOnlyDataService),
|
[ITEM_TYPE.value, () => import('./data/href-only-data.service').then(m => m.HrefOnlyDataService)],
|
||||||
[LICENSE.value]: () => import('./data/href-only-data.service').then(m => m.HrefOnlyDataService),
|
[LICENSE.value, () => import('./data/href-only-data.service').then(m => m.HrefOnlyDataService)],
|
||||||
[SUBSCRIPTION.value]: () => import('../shared/subscriptions/subscriptions-data.service').then(m => m.SubscriptionsDataService),
|
[SUBSCRIPTION.value, () => import('../shared/subscriptions/subscriptions-data.service').then(m => m.SubscriptionsDataService)],
|
||||||
[COMMUNITY.value]: () => import('./data/community-data.service').then(m => m.CommunityDataService),
|
[COMMUNITY.value, () => import('./data/community-data.service').then(m => m.CommunityDataService)],
|
||||||
[VOCABULARY.value]: () => import('./submission/vocabularies/vocabulary.data.service').then(m => m.VocabularyDataService),
|
[VOCABULARY.value, () => import('./submission/vocabularies/vocabulary.data.service').then(m => m.VocabularyDataService)],
|
||||||
[BUNDLE.value]: () => import('./data/bundle-data.service').then(m => m.BundleDataService),
|
[BUNDLE.value, () => import('./data/bundle-data.service').then(m => m.BundleDataService)],
|
||||||
[CONFIG_PROPERTY.value]: () => import('./data/configuration-data.service').then(m => m.ConfigurationDataService),
|
[CONFIG_PROPERTY.value, () => import('./data/configuration-data.service').then(m => m.ConfigurationDataService)],
|
||||||
[POOL_TASK.value]: () => import('./tasks/pool-task-data.service').then(m => m.PoolTaskDataService),
|
[POOL_TASK.value, () => import('./tasks/pool-task-data.service').then(m => m.PoolTaskDataService)],
|
||||||
[CLAIMED_TASK.value]: () => import('./tasks/claimed-task-data.service').then(m => m.ClaimedTaskDataService),
|
[CLAIMED_TASK.value, () => import('./tasks/claimed-task-data.service').then(m => m.ClaimedTaskDataService)],
|
||||||
[SUPERVISION_ORDER.value]: () => import('./supervision-order/supervision-order-data.service').then(m => m.SupervisionOrderDataService),
|
[SUPERVISION_ORDER.value, () => import('./supervision-order/supervision-order-data.service').then(m => m.SupervisionOrderDataService)],
|
||||||
[WORKSPACEITEM.value]: () => import('./submission/workspaceitem-data.service').then(m => m.WorkspaceitemDataService),
|
[WORKSPACEITEM.value, () => import('./submission/workspaceitem-data.service').then(m => m.WorkspaceitemDataService)],
|
||||||
[WORKFLOWITEM.value]: () => import('./submission/workflowitem-data.service').then(m => m.WorkflowItemDataService),
|
[WORKFLOWITEM.value, () => import('./submission/workflowitem-data.service').then(m => m.WorkflowItemDataService)],
|
||||||
[VOCABULARY.value]: () => import('./submission/vocabularies/vocabulary.data.service').then(m => m.VocabularyDataService),
|
[VOCABULARY.value, () => import('./submission/vocabularies/vocabulary.data.service').then(m => m.VocabularyDataService)],
|
||||||
[VOCABULARY_ENTRY_DETAIL.value]: () => import('./submission/vocabularies/vocabulary-entry-details.data.service').then(m => m.VocabularyEntryDetailsDataService),
|
[VOCABULARY_ENTRY_DETAIL.value, () => import('./submission/vocabularies/vocabulary-entry-details.data.service').then(m => m.VocabularyEntryDetailsDataService)],
|
||||||
[SUBMISSION_CC_LICENSE_URL.value]: () => import('./submission/submission-cc-license-url-data.service').then(m => m.SubmissionCcLicenseUrlDataService),
|
[SUBMISSION_CC_LICENSE_URL.value, () => import('./submission/submission-cc-license-url-data.service').then(m => m.SubmissionCcLicenseUrlDataService)],
|
||||||
[SUBMISSION_CC_LICENSE.value]: () => import('./submission/submission-cc-license-data.service').then(m => m.SubmissionCcLicenseDataService),
|
[SUBMISSION_CC_LICENSE.value, () => import('./submission/submission-cc-license-data.service').then(m => m.SubmissionCcLicenseDataService)],
|
||||||
[USAGE_REPORT.value]: () => import('./statistics/usage-report-data.service').then(m => m.UsageReportDataService),
|
[USAGE_REPORT.value, () => import('./statistics/usage-report-data.service').then(m => m.UsageReportDataService)],
|
||||||
[RESOURCE_POLICY.value]: () => import('./resource-policy/resource-policy-data.service').then(m => m.ResourcePolicyDataService),
|
[RESOURCE_POLICY.value, () => import('./resource-policy/resource-policy-data.service').then(m => m.ResourcePolicyDataService)],
|
||||||
[RESEARCHER_PROFILE.value]: () => import('./profile/researcher-profile-data.service').then(m => m.ResearcherProfileDataService),
|
[RESEARCHER_PROFILE.value, () => import('./profile/researcher-profile-data.service').then(m => m.ResearcherProfileDataService)],
|
||||||
[ORCID_QUEUE.value]: () => import('./orcid/orcid-queue-data.service').then(m => m.OrcidQueueDataService),
|
[ORCID_QUEUE.value, () => import('./orcid/orcid-queue-data.service').then(m => m.OrcidQueueDataService)],
|
||||||
[ORCID_HISTORY.value]: () => import('./orcid/orcid-history-data.service').then(m => m.OrcidHistoryDataService),
|
[ORCID_HISTORY.value, () => import('./orcid/orcid-history-data.service').then(m => m.OrcidHistoryDataService)],
|
||||||
[FEEDBACK.value]: () => import('./feedback/feedback-data.service').then(m => m.FeedbackDataService),
|
[FEEDBACK.value, () => import('./feedback/feedback-data.service').then(m => m.FeedbackDataService)],
|
||||||
[GROUP.value]: () => import('./eperson/group-data.service').then(m => m.GroupDataService),
|
[GROUP.value, () => import('./eperson/group-data.service').then(m => m.GroupDataService)],
|
||||||
[EPERSON.value]: () => import('./eperson/eperson-data.service').then(m => m.EPersonDataService),
|
[EPERSON.value, () => import('./eperson/eperson-data.service').then(m => m.EPersonDataService)],
|
||||||
[WORKFLOW_ACTION.value]: () => import('./data/workflow-action-data.service').then(m => m.WorkflowActionDataService),
|
[WORKFLOW_ACTION.value, () => import('./data/workflow-action-data.service').then(m => m.WorkflowActionDataService)],
|
||||||
[VERSION_HISTORY.value]: () => import('./data/version-history-data.service').then(m => m.VersionHistoryDataService),
|
[VERSION_HISTORY.value, () => import('./data/version-history-data.service').then(m => m.VersionHistoryDataService)],
|
||||||
[SITE.value]: () => import('./data/site-data.service').then(m => m.SiteDataService),
|
[SITE.value, () => import('./data/site-data.service').then(m => m.SiteDataService)],
|
||||||
[ROOT.value]: () => import('./data/root-data.service').then(m => m.RootDataService),
|
[ROOT.value, () => import('./data/root-data.service').then(m => m.RootDataService)],
|
||||||
[RELATIONSHIP_TYPE.value]: () => import('./data/relationship-type-data.service').then(m => m.RelationshipTypeDataService),
|
[RELATIONSHIP_TYPE.value, () => import('./data/relationship-type-data.service').then(m => m.RelationshipTypeDataService)],
|
||||||
[RELATIONSHIP.value]: () => import('./data/relationship-data.service').then(m => m.RelationshipDataService),
|
[RELATIONSHIP.value, () => import('./data/relationship-data.service').then(m => m.RelationshipDataService)],
|
||||||
[SCRIPT.value]: () => import('./data/processes/script-data.service').then(m => m.ScriptDataService),
|
[SCRIPT.value, () => import('./data/processes/script-data.service').then(m => m.ScriptDataService)],
|
||||||
[PROCESS.value]: () => import('./data/processes/process-data.service').then(m => m.ProcessDataService),
|
[PROCESS.value, () => import('./data/processes/process-data.service').then(m => m.ProcessDataService)],
|
||||||
[METADATA_FIELD.value]: () => import('./data/metadata-field-data.service').then(m => m.MetadataFieldDataService),
|
[METADATA_FIELD.value, () => import('./data/metadata-field-data.service').then(m => m.MetadataFieldDataService)],
|
||||||
[ITEM.value]: () => import('./data/item-data.service').then(m => m.ItemDataService),
|
[ITEM.value, () => import('./data/item-data.service').then(m => m.ItemDataService)],
|
||||||
[VERSION.value]: () => import('./data/version-data.service').then(m => m.VersionDataService),
|
[VERSION.value, () => import('./data/version-data.service').then(m => m.VersionDataService)],
|
||||||
[IDENTIFIERS.value]: () => import('./data/identifier-data.service').then(m => m.IdentifierDataService),
|
[IDENTIFIERS.value, () => import('./data/identifier-data.service').then(m => m.IdentifierDataService)],
|
||||||
[FEATURE.value]: () => import('./data/feature-authorization/authorization-data.service').then(m => m.AuthorizationDataService),
|
[FEATURE.value, () => import('./data/feature-authorization/authorization-data.service').then(m => m.AuthorizationDataService)],
|
||||||
[DSPACE_OBJECT.value]: () => import('./data/dspace-object-data.service').then(m => m.DSpaceObjectDataService),
|
[DSPACE_OBJECT.value, () => import('./data/dspace-object-data.service').then(m => m.DSpaceObjectDataService)],
|
||||||
[BITSTREAM_FORMAT.value]: () => import('./data/bitstream-format-data.service').then(m => m.BitstreamFormatDataService),
|
[BITSTREAM_FORMAT.value, () => import('./data/bitstream-format-data.service').then(m => m.BitstreamFormatDataService)],
|
||||||
[SUBMISSION_COAR_NOTIFY_CONFIG.value]: () => import('../submission/sections/section-coar-notify/coar-notify-config-data.service').then(m => m.CoarNotifyConfigDataService),
|
[SUBMISSION_COAR_NOTIFY_CONFIG.value, () => import('../submission/sections/section-coar-notify/coar-notify-config-data.service').then(m => m.CoarNotifyConfigDataService)],
|
||||||
[LDN_SERVICE_CONSTRAINT_FILTERS.value]: () => import('../admin/admin-ldn-services/ldn-services-data/ldn-itemfilters-data.service').then(m => m.LdnItemfiltersService),
|
[LDN_SERVICE_CONSTRAINT_FILTERS.value, () => import('../admin/admin-ldn-services/ldn-services-data/ldn-itemfilters-data.service').then(m => m.LdnItemfiltersService)],
|
||||||
[LDN_SERVICE.value]: () => import('../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service').then(m => m.LdnServicesService),
|
[LDN_SERVICE.value, () => import('../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service').then(m => m.LdnServicesService)],
|
||||||
[ADMIN_NOTIFY_MESSAGE.value]: () => import('../admin/admin-notify-dashboard/services/admin-notify-messages.service').then(m => m.AdminNotifyMessagesService),
|
[ADMIN_NOTIFY_MESSAGE.value, () => import('../admin/admin-notify-dashboard/services/admin-notify-messages.service').then(m => m.AdminNotifyMessagesService)],
|
||||||
[SUBMISSION_FORMS_TYPE.value]: () => import('./config/submission-forms-config-data.service').then(m => m.SubmissionFormsConfigDataService),
|
[SUBMISSION_FORMS_TYPE.value, () => import('./config/submission-forms-config-data.service').then(m => m.SubmissionFormsConfigDataService)],
|
||||||
[NOTIFYREQUEST.value]: () => import('./data/notify-services-status-data.service').then(m => m.NotifyRequestsStatusDataService),
|
[NOTIFYREQUEST.value, () => import('./data/notify-services-status-data.service').then(m => m.NotifyRequestsStatusDataService)],
|
||||||
[QUALITY_ASSURANCE_EVENT_OBJECT.value]: () => import('./notifications/qa/events/quality-assurance-event-data.service').then(m => m.QualityAssuranceEventDataService),
|
[QUALITY_ASSURANCE_EVENT_OBJECT.value, () => import('./notifications/qa/events/quality-assurance-event-data.service').then(m => m.QualityAssuranceEventDataService)],
|
||||||
[QUALITY_ASSURANCE_SOURCE_OBJECT.value]: () => import('./notifications/qa/source/quality-assurance-source-data.service').then(m => m.QualityAssuranceSourceDataService),
|
[QUALITY_ASSURANCE_SOURCE_OBJECT.value, () => import('./notifications/qa/source/quality-assurance-source-data.service').then(m => m.QualityAssuranceSourceDataService)],
|
||||||
[QUALITY_ASSURANCE_TOPIC_OBJECT.value]: () => import('./notifications/qa/topics/quality-assurance-topic-data.service').then(m => m.QualityAssuranceTopicDataService),
|
[QUALITY_ASSURANCE_TOPIC_OBJECT.value, () => import('./notifications/qa/topics/quality-assurance-topic-data.service').then(m => m.QualityAssuranceTopicDataService)],
|
||||||
[SUGGESTION.value]: () => import('./notifications/suggestions-data.service').then(m => m.SuggestionsDataService),
|
[SUGGESTION.value, () => import('./notifications/suggestions-data.service').then(m => m.SuggestionsDataService)],
|
||||||
[SUGGESTION_SOURCE.value]: () => import('./notifications/source/suggestion-source-data.service').then(m => m.SuggestionSourceDataService),
|
[SUGGESTION_SOURCE.value, () => import('./notifications/source/suggestion-source-data.service').then(m => m.SuggestionSourceDataService)],
|
||||||
[SUGGESTION_TARGET.value]: () => import('./notifications/target/suggestion-target-data.service').then(m => m.SuggestionTargetDataService),
|
[SUGGESTION_TARGET.value, () => import('./notifications/target/suggestion-target-data.service').then(m => m.SuggestionTargetDataService)],
|
||||||
[DUPLICATE.value]: () => import('./submission/submission-duplicate-data.service').then(m => m.SubmissionDuplicateDataService),
|
[DUPLICATE.value, () => import('./submission/submission-duplicate-data.service').then(m => m.SubmissionDuplicateDataService)],
|
||||||
[CorrectionType.type.value]: () => import('./submission/correctiontype-data.service').then(m => m.CorrectionTypeDataService),
|
[CorrectionType.type.value, () => import('./submission/correctiontype-data.service').then(m => m.CorrectionTypeDataService)],
|
||||||
};
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
@@ -28,7 +28,10 @@ import { Community } from '../shared/community.model';
|
|||||||
import { ContentSource } from '../shared/content-source.model';
|
import { ContentSource } from '../shared/content-source.model';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
import {
|
||||||
|
getAllCompletedRemoteData,
|
||||||
|
getFirstCompletedRemoteData,
|
||||||
|
} from '../shared/operators';
|
||||||
import { BitstreamDataService } from './bitstream-data.service';
|
import { BitstreamDataService } from './bitstream-data.service';
|
||||||
import { ComColDataService } from './comcol-data.service';
|
import { ComColDataService } from './comcol-data.service';
|
||||||
import { CommunityDataService } from './community-data.service';
|
import { CommunityDataService } from './community-data.service';
|
||||||
@@ -84,7 +87,8 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this.searchBy(searchHref, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe(
|
return this.searchBy(searchHref, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe(
|
||||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
getAllCompletedRemoteData(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +118,8 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe(
|
return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe(
|
||||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
getAllCompletedRemoteData(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,7 +143,8 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this.searchBy(searchHref, options, reRequestOnStale).pipe(
|
return this.searchBy(searchHref, options, reRequestOnStale).pipe(
|
||||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
getAllCompletedRemoteData(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get all collections the user is authorized to submit to, by community and has the metadata
|
* Get all collections the user is authorized to submit to, by community and has the metadata
|
||||||
@@ -169,7 +175,8 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe(
|
return this.searchBy(searchHref, options, true, reRequestOnStale, ...linksToFollow).pipe(
|
||||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
getAllCompletedRemoteData(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,9 +191,8 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
options.elementsPerPage = 1;
|
options.elementsPerPage = 1;
|
||||||
|
|
||||||
return this.searchBy(searchHref, options).pipe(
|
return this.searchBy(searchHref, options).pipe(
|
||||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending),
|
getFirstCompletedRemoteData(),
|
||||||
take(1),
|
map((collections: RemoteData<PaginatedList<Collection>>) => collections?.payload?.totalElements > 0),
|
||||||
map((collections: RemoteData<PaginatedList<Collection>>) => collections.payload.totalElements > 0),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -91,7 +91,7 @@ export class DsoRedirectService {
|
|||||||
/**
|
/**
|
||||||
* Redirect to a DSpaceObject's path using the given identifier type and ID.
|
* Redirect to a DSpaceObject's path using the given identifier type and ID.
|
||||||
* This is used to redirect paths like "/handle/[prefix]/[suffix]" to the object's path (e.g. /items/[uuid]).
|
* This is used to redirect paths like "/handle/[prefix]/[suffix]" to the object's path (e.g. /items/[uuid]).
|
||||||
* See LookupGuard for more examples.
|
* See lookupGuard for more examples.
|
||||||
*
|
*
|
||||||
* @param id the identifier of the object to retrieve
|
* @param id the identifier of the object to retrieve
|
||||||
* @param identifierType the type of the given identifier (defaults to UUID)
|
* @param identifierType the type of the given identifier (defaults to UUID)
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
filter,
|
|
||||||
map,
|
map,
|
||||||
switchMap,
|
switchMap,
|
||||||
take,
|
take,
|
||||||
@@ -14,6 +13,7 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
|
|||||||
import { ItemType } from '../shared/item-relationships/item-type.model';
|
import { ItemType } from '../shared/item-relationships/item-type.model';
|
||||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||||
import {
|
import {
|
||||||
|
getAllCompletedRemoteData,
|
||||||
getFirstSucceededRemoteData,
|
getFirstSucceededRemoteData,
|
||||||
getRemoteDataPayload,
|
getRemoteDataPayload,
|
||||||
} from '../shared/operators';
|
} from '../shared/operators';
|
||||||
@@ -89,8 +89,7 @@ export class EntityTypeDataService extends BaseDataService<ItemType> implements
|
|||||||
getAllAuthorizedRelationshipType(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<ItemType>>> {
|
getAllAuthorizedRelationshipType(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<ItemType>>> {
|
||||||
const searchHref = 'findAllByAuthorizedCollection';
|
const searchHref = 'findAllByAuthorizedCollection';
|
||||||
|
|
||||||
return this.searchBy(searchHref, options).pipe(
|
return this.searchBy(searchHref, options).pipe(getAllCompletedRemoteData());
|
||||||
filter((type: RemoteData<PaginatedList<ItemType>>) => !type.isResponsePending));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,8 +122,7 @@ export class EntityTypeDataService extends BaseDataService<ItemType> implements
|
|||||||
getAllAuthorizedRelationshipTypeImport(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<ItemType>>> {
|
getAllAuthorizedRelationshipTypeImport(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<ItemType>>> {
|
||||||
const searchHref = 'findAllByAuthorizedExternalSource';
|
const searchHref = 'findAllByAuthorizedExternalSource';
|
||||||
|
|
||||||
return this.searchBy(searchHref, options).pipe(
|
return this.searchBy(searchHref, options).pipe(getAllCompletedRemoteData());
|
||||||
filter((type: RemoteData<PaginatedList<ItemType>>) => !type.isResponsePending));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,15 +134,8 @@ export class EntityTypeDataService extends BaseDataService<ItemType> implements
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
};
|
};
|
||||||
return this.getAllAuthorizedRelationshipTypeImport(findListOptions).pipe(
|
return this.getAllAuthorizedRelationshipTypeImport(findListOptions).pipe(
|
||||||
map((result: RemoteData<PaginatedList<ItemType>>) => {
|
take(1),
|
||||||
let output: boolean;
|
map((result: RemoteData<PaginatedList<ItemType>>) => result?.payload?.totalElements > 1),
|
||||||
if (result.payload) {
|
|
||||||
output = ( result.payload.page.length > 1 );
|
|
||||||
} else {
|
|
||||||
output = false;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -12,21 +12,30 @@ import {
|
|||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
import { DSpaceObject } from '../../../shared/dspace-object.model';
|
import { DSpaceObject } from '../../../shared/dspace-object.model';
|
||||||
|
import { Item } from '../../../shared/item.model';
|
||||||
import { RemoteData } from '../../remote-data';
|
import { RemoteData } from '../../remote-data';
|
||||||
import { AuthorizationDataService } from '../authorization-data.service';
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { FeatureID } from '../feature-id';
|
import { FeatureID } from '../feature-id';
|
||||||
import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard';
|
import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard';
|
||||||
|
|
||||||
|
const object = {
|
||||||
|
self: 'test-selflink',
|
||||||
|
} as DSpaceObject;
|
||||||
|
|
||||||
|
const testResolver: ResolveFn<RemoteData<any>> = () => createSuccessfulRemoteDataObject$(object);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test implementation of abstract class DsoPageSingleFeatureGuard
|
* Test implementation of abstract class DsoPageSingleFeatureGuard
|
||||||
*/
|
*/
|
||||||
class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard<any> {
|
class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard<any> {
|
||||||
constructor(protected resolver: Resolve<RemoteData<any>>,
|
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected resolver: ResolveFn<RemoteData<Item>> = testResolver;
|
||||||
|
|
||||||
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService,
|
protected authService: AuthService,
|
||||||
protected featureID: FeatureID) {
|
protected featureID: FeatureID) {
|
||||||
super(resolver, authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
|
getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
|
||||||
@@ -39,25 +48,16 @@ describe('DsoPageSingleFeatureGuard', () => {
|
|||||||
let authorizationService: AuthorizationDataService;
|
let authorizationService: AuthorizationDataService;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
let resolver: Resolve<RemoteData<any>>;
|
|
||||||
let object: DSpaceObject;
|
|
||||||
let route;
|
let route;
|
||||||
let parentRoute;
|
let parentRoute;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
object = {
|
|
||||||
self: 'test-selflink',
|
|
||||||
} as DSpaceObject;
|
|
||||||
|
|
||||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
isAuthorized: observableOf(true),
|
isAuthorized: observableOf(true),
|
||||||
});
|
});
|
||||||
router = jasmine.createSpyObj('router', {
|
router = jasmine.createSpyObj('router', {
|
||||||
parseUrl: {},
|
parseUrl: {},
|
||||||
});
|
});
|
||||||
resolver = jasmine.createSpyObj('resolver', {
|
|
||||||
resolve: createSuccessfulRemoteDataObject$(object),
|
|
||||||
});
|
|
||||||
authService = jasmine.createSpyObj('authService', {
|
authService = jasmine.createSpyObj('authService', {
|
||||||
isAuthenticated: observableOf(true),
|
isAuthenticated: observableOf(true),
|
||||||
});
|
});
|
||||||
@@ -71,7 +71,7 @@ describe('DsoPageSingleFeatureGuard', () => {
|
|||||||
},
|
},
|
||||||
parent: parentRoute,
|
parent: parentRoute,
|
||||||
};
|
};
|
||||||
guard = new DsoPageSingleFeatureGuardImpl(resolver, authorizationService, router, authService, undefined);
|
guard = new DsoPageSingleFeatureGuardImpl(authorizationService, router, authService, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -12,21 +12,30 @@ import {
|
|||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
import { DSpaceObject } from '../../../shared/dspace-object.model';
|
import { DSpaceObject } from '../../../shared/dspace-object.model';
|
||||||
|
import { Item } from '../../../shared/item.model';
|
||||||
import { RemoteData } from '../../remote-data';
|
import { RemoteData } from '../../remote-data';
|
||||||
import { AuthorizationDataService } from '../authorization-data.service';
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { FeatureID } from '../feature-id';
|
import { FeatureID } from '../feature-id';
|
||||||
import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard';
|
import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard';
|
||||||
|
|
||||||
|
const object = {
|
||||||
|
self: 'test-selflink',
|
||||||
|
} as DSpaceObject;
|
||||||
|
|
||||||
|
const testResolver: ResolveFn<RemoteData<any>> = () => createSuccessfulRemoteDataObject$(object);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test implementation of abstract class DsoPageSomeFeatureGuard
|
* Test implementation of abstract class DsoPageSomeFeatureGuard
|
||||||
*/
|
*/
|
||||||
class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard<any> {
|
class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard<any> {
|
||||||
constructor(protected resolver: Resolve<RemoteData<any>>,
|
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected resolver: ResolveFn<RemoteData<Item>> = testResolver;
|
||||||
|
|
||||||
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService,
|
protected authService: AuthService,
|
||||||
protected featureIDs: FeatureID[]) {
|
protected featureIDs: FeatureID[]) {
|
||||||
super(resolver, authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
|
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
|
||||||
@@ -39,25 +48,17 @@ describe('DsoPageSomeFeatureGuard', () => {
|
|||||||
let authorizationService: AuthorizationDataService;
|
let authorizationService: AuthorizationDataService;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
let resolver: Resolve<RemoteData<any>>;
|
|
||||||
let object: DSpaceObject;
|
|
||||||
let route;
|
let route;
|
||||||
let parentRoute;
|
let parentRoute;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
object = {
|
|
||||||
self: 'test-selflink',
|
|
||||||
} as DSpaceObject;
|
|
||||||
|
|
||||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
isAuthorized: observableOf(true),
|
isAuthorized: observableOf(true),
|
||||||
});
|
});
|
||||||
router = jasmine.createSpyObj('router', {
|
router = jasmine.createSpyObj('router', {
|
||||||
parseUrl: {},
|
parseUrl: {},
|
||||||
});
|
});
|
||||||
resolver = jasmine.createSpyObj('resolver', {
|
|
||||||
resolve: createSuccessfulRemoteDataObject$(object),
|
|
||||||
});
|
|
||||||
authService = jasmine.createSpyObj('authService', {
|
authService = jasmine.createSpyObj('authService', {
|
||||||
isAuthenticated: observableOf(true),
|
isAuthenticated: observableOf(true),
|
||||||
});
|
});
|
||||||
@@ -71,7 +72,7 @@ describe('DsoPageSomeFeatureGuard', () => {
|
|||||||
},
|
},
|
||||||
parent: parentRoute,
|
parent: parentRoute,
|
||||||
};
|
};
|
||||||
guard = new DsoPageSomeFeatureGuardImpl(resolver, authorizationService, router, authService, []);
|
guard = new DsoPageSomeFeatureGuardImpl(authorizationService, router, authService, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
Resolve,
|
ResolveFn,
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -23,8 +23,10 @@ import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guar
|
|||||||
* This guard utilizes a resolver to retrieve the relevant object to check authorizations for
|
* This guard utilizes a resolver to retrieve the relevant object to check authorizations for
|
||||||
*/
|
*/
|
||||||
export abstract class DsoPageSomeFeatureGuard<T extends DSpaceObject> extends SomeFeatureAuthorizationGuard {
|
export abstract class DsoPageSomeFeatureGuard<T extends DSpaceObject> extends SomeFeatureAuthorizationGuard {
|
||||||
constructor(protected resolver: Resolve<RemoteData<T>>,
|
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected abstract resolver: ResolveFn<RemoteData<DSpaceObject>>;
|
||||||
|
|
||||||
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService) {
|
protected authService: AuthService) {
|
||||||
super(authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
@@ -35,14 +37,14 @@ export abstract class DsoPageSomeFeatureGuard<T extends DSpaceObject> extends So
|
|||||||
*/
|
*/
|
||||||
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
|
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
|
||||||
const routeWithObjectID = this.getRouteWithDSOId(route);
|
const routeWithObjectID = this.getRouteWithDSOId(route);
|
||||||
return (this.resolver.resolve(routeWithObjectID, state) as Observable<RemoteData<T>>).pipe(
|
return (this.resolver(routeWithObjectID, state) as Observable<RemoteData<T>>).pipe(
|
||||||
getAllSucceededRemoteDataPayload(),
|
getAllSucceededRemoteDataPayload(),
|
||||||
map((dso) => dso.self),
|
map((dso) => dso.self),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to resolve resolve (parent) route that contains the UUID of the DSO
|
* Method to resolve (parent) route that contains the UUID of the DSO
|
||||||
* @param route The current route
|
* @param route The current route
|
||||||
*/
|
*/
|
||||||
protected getRouteWithDSOId(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot {
|
protected getRouteWithDSOId(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot {
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
UrlTree,
|
UrlTree,
|
||||||
@@ -22,7 +21,7 @@ import { FeatureID } from '../feature-id';
|
|||||||
* doesn't have authorized rights on any of the specified features and/or object.
|
* doesn't have authorized rights on any of the specified features and/or object.
|
||||||
* Override the desired getters in the parent class for checking specific authorization on a list of features and/or object.
|
* Override the desired getters in the parent class for checking specific authorization on a list of features and/or object.
|
||||||
*/
|
*/
|
||||||
export abstract class SomeFeatureAuthorizationGuard implements CanActivate {
|
export abstract class SomeFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService,
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService) {
|
protected authService: AuthService) {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Injector } from '@angular/core';
|
import { Injector } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
import { createMockStore } from '@ngrx/store/testing';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
import { Notification } from '../../../shared/notifications/models/notification.model';
|
import { Notification } from '../../../shared/notifications/models/notification.model';
|
||||||
@@ -52,7 +53,7 @@ describe('ObjectUpdatesService', () => {
|
|||||||
const objectEntry = {
|
const objectEntry = {
|
||||||
fieldStates, fieldUpdates, lastModified: modDate, virtualMetadataSources: {}, patchOperationService,
|
fieldStates, fieldUpdates, lastModified: modDate, virtualMetadataSources: {}, patchOperationService,
|
||||||
};
|
};
|
||||||
store = new Store<CoreState>(undefined, undefined, undefined);
|
store = createMockStore({});
|
||||||
spyOn(store, 'dispatch');
|
spyOn(store, 'dispatch');
|
||||||
injector = jasmine.createSpyObj('injector', {
|
injector = jasmine.createSpyObj('injector', {
|
||||||
get: patchOperationService,
|
get: patchOperationService,
|
||||||
|
@@ -13,6 +13,7 @@ import {
|
|||||||
map,
|
map,
|
||||||
mergeMap,
|
mergeMap,
|
||||||
take,
|
take,
|
||||||
|
withLatestFrom,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -25,6 +26,7 @@ import { ParsedResponse } from '../cache/response.models';
|
|||||||
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
|
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
|
||||||
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 { XSRFService } from '../xsrf/xsrf.service';
|
||||||
import {
|
import {
|
||||||
RequestActionTypes,
|
RequestActionTypes,
|
||||||
RequestErrorAction,
|
RequestErrorAction,
|
||||||
@@ -35,6 +37,7 @@ import {
|
|||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { RequestEntry } from './request-entry.model';
|
import { RequestEntry } from './request-entry.model';
|
||||||
import { RequestError } from './request-error.model';
|
import { RequestError } from './request-error.model';
|
||||||
|
import { RestRequestMethod } from './rest-request-method';
|
||||||
import { RestRequestWithResponseParser } from './rest-request-with-response-parser.model';
|
import { RestRequestWithResponseParser } from './rest-request-with-response-parser.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -48,7 +51,11 @@ export class RequestEffects {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
filter((entry: RequestEntry) => hasValue(entry)),
|
filter((entry: RequestEntry) => hasValue(entry)),
|
||||||
map((entry: RequestEntry) => entry.request),
|
withLatestFrom(this.xsrfService.tokenInitialized$),
|
||||||
|
// If it's a GET request, or we have an XSRF token, dispatch it immediately
|
||||||
|
// Otherwise wait for the XSRF token first
|
||||||
|
filter(([entry, tokenInitialized]: [RequestEntry, boolean]) => entry.request.method === RestRequestMethod.GET || tokenInitialized === true),
|
||||||
|
map(([entry, tokenInitialized]: [RequestEntry, boolean]) => entry.request),
|
||||||
mergeMap((request: RestRequestWithResponseParser) => {
|
mergeMap((request: RestRequestWithResponseParser) => {
|
||||||
let body = request.body;
|
let body = request.body;
|
||||||
if (isNotEmpty(request.body) && !request.isMultipart) {
|
if (isNotEmpty(request.body) && !request.isMultipart) {
|
||||||
@@ -89,6 +96,7 @@ export class RequestEffects {
|
|||||||
private restApi: DspaceRestService,
|
private restApi: DspaceRestService,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
|
protected xsrfService: XSRFService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
fakeAsync,
|
fakeAsync,
|
||||||
|
flush,
|
||||||
TestBed,
|
TestBed,
|
||||||
waitForAsync,
|
waitForAsync,
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
@@ -104,11 +105,11 @@ describe('RequestService', () => {
|
|||||||
store = TestBed.inject(Store);
|
store = TestBed.inject(Store);
|
||||||
mockStore = store as MockStore<CoreState>;
|
mockStore = store as MockStore<CoreState>;
|
||||||
mockStore.setState(initialState);
|
mockStore.setState(initialState);
|
||||||
|
|
||||||
service = new RequestService(
|
service = new RequestService(
|
||||||
objectCache,
|
objectCache,
|
||||||
uuidService,
|
uuidService,
|
||||||
store,
|
store,
|
||||||
undefined,
|
|
||||||
);
|
);
|
||||||
serviceAsAny = service as any;
|
serviceAsAny = service as any;
|
||||||
});
|
});
|
||||||
@@ -501,21 +502,23 @@ describe('RequestService', () => {
|
|||||||
dispatchSpy = spyOn(store, 'dispatch');
|
dispatchSpy = spyOn(store, 'dispatch');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dispatch a RequestConfigureAction', () => {
|
it('should dispatch a RequestConfigureAction', fakeAsync(() => {
|
||||||
const request = testGetRequest;
|
const request = testGetRequest;
|
||||||
serviceAsAny.dispatchRequest(request);
|
serviceAsAny.dispatchRequest(request);
|
||||||
|
flush();
|
||||||
const firstAction = dispatchSpy.calls.argsFor(0)[0];
|
const firstAction = dispatchSpy.calls.argsFor(0)[0];
|
||||||
expect(firstAction).toBeInstanceOf(RequestConfigureAction);
|
expect(firstAction).toBeInstanceOf(RequestConfigureAction);
|
||||||
expect(firstAction.payload).toEqual(request);
|
expect(firstAction.payload).toEqual(request);
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should dispatch a RequestExecuteAction', () => {
|
it('should dispatch a RequestExecuteAction', fakeAsync(() => {
|
||||||
const request = testGetRequest;
|
const request = testGetRequest;
|
||||||
serviceAsAny.dispatchRequest(request);
|
serviceAsAny.dispatchRequest(request);
|
||||||
|
flush();
|
||||||
const secondAction = dispatchSpy.calls.argsFor(1)[0];
|
const secondAction = dispatchSpy.calls.argsFor(1)[0];
|
||||||
expect(secondAction).toBeInstanceOf(RequestExecuteAction);
|
expect(secondAction).toBeInstanceOf(RequestExecuteAction);
|
||||||
expect(secondAction.payload).toEqual(request.uuid);
|
expect(secondAction.payload).toEqual(request.uuid);
|
||||||
});
|
}));
|
||||||
|
|
||||||
describe('when it\'s not a GET request', () => {
|
describe('when it\'s not a GET request', () => {
|
||||||
it('shouldn\'t track it', () => {
|
it('shouldn\'t track it', () => {
|
||||||
|
@@ -8,6 +8,7 @@ import {
|
|||||||
} from '@ngrx/store';
|
} from '@ngrx/store';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import {
|
import {
|
||||||
|
asapScheduler,
|
||||||
from as observableFrom,
|
from as observableFrom,
|
||||||
Observable,
|
Observable,
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
@@ -33,10 +34,7 @@ import { ObjectCacheService } from '../cache/object-cache.service';
|
|||||||
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
||||||
import { coreSelector } from '../core.selectors';
|
import { coreSelector } from '../core.selectors';
|
||||||
import { CoreState } from '../core-state.model';
|
import { CoreState } from '../core-state.model';
|
||||||
import {
|
import { IndexState } from '../index/index.reducer';
|
||||||
IndexState,
|
|
||||||
MetaIndexState,
|
|
||||||
} from '../index/index.reducer';
|
|
||||||
import {
|
import {
|
||||||
getUrlWithoutEmbedParams,
|
getUrlWithoutEmbedParams,
|
||||||
requestIndexSelector,
|
requestIndexSelector,
|
||||||
@@ -167,8 +165,7 @@ export class RequestService {
|
|||||||
|
|
||||||
constructor(private objectCache: ObjectCacheService,
|
constructor(private objectCache: ObjectCacheService,
|
||||||
private uuidService: UUIDService,
|
private uuidService: UUIDService,
|
||||||
private store: Store<CoreState>,
|
private store: Store<CoreState>) {
|
||||||
private indexStore: Store<MetaIndexState>) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generateRequestId(): string {
|
generateRequestId(): string {
|
||||||
@@ -241,7 +238,7 @@ export class RequestService {
|
|||||||
return source.pipe(
|
return source.pipe(
|
||||||
tap((entry: RequestEntry) => {
|
tap((entry: RequestEntry) => {
|
||||||
if (hasValue(entry) && hasValue(entry.request) && !isStale(entry.state) && !isValid(entry)) {
|
if (hasValue(entry) && hasValue(entry.request) && !isStale(entry.state) && !isValid(entry)) {
|
||||||
this.store.dispatch(new RequestStaleAction(entry.request.uuid));
|
asapScheduler.schedule(() => this.store.dispatch(new RequestStaleAction(entry.request.uuid)));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -394,6 +391,7 @@ export class RequestService {
|
|||||||
const requestEntry$ = this.getByHref(href);
|
const requestEntry$ = this.getByHref(href);
|
||||||
|
|
||||||
requestEntry$.pipe(
|
requestEntry$.pipe(
|
||||||
|
filter((re: RequestEntry) => isNotEmpty(re)),
|
||||||
map((re: RequestEntry) => re.request.uuid),
|
map((re: RequestEntry) => re.request.uuid),
|
||||||
take(1),
|
take(1),
|
||||||
).subscribe((uuid: string) => {
|
).subscribe((uuid: string) => {
|
||||||
@@ -449,8 +447,10 @@ export class RequestService {
|
|||||||
* @param {RestRequest} request to dispatch
|
* @param {RestRequest} request to dispatch
|
||||||
*/
|
*/
|
||||||
private dispatchRequest(request: RestRequest) {
|
private dispatchRequest(request: RestRequest) {
|
||||||
|
asapScheduler.schedule(() => {
|
||||||
this.store.dispatch(new RequestConfigureAction(request));
|
this.store.dispatch(new RequestConfigureAction(request));
|
||||||
this.store.dispatch(new RequestExecuteAction(request.uuid));
|
this.store.dispatch(new RequestExecuteAction(request.uuid));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
|
||||||
Router,
|
Router,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
UrlTree,
|
UrlTree,
|
||||||
@@ -17,7 +16,7 @@ import { returnEndUserAgreementUrlTreeOnFalse } from '../shared/authorized.opera
|
|||||||
* An abstract guard for redirecting users to the user agreement page if a certain condition is met
|
* An abstract guard for redirecting users to the user agreement page if a certain condition is met
|
||||||
* That condition is defined by abstract method hasAccepted
|
* That condition is defined by abstract method hasAccepted
|
||||||
*/
|
*/
|
||||||
export abstract class AbstractEndUserAgreementGuard implements CanActivate {
|
export abstract class AbstractEndUserAgreementGuard {
|
||||||
|
|
||||||
constructor(protected router: Router) {
|
constructor(protected router: Router) {
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import {
|
|||||||
Store,
|
Store,
|
||||||
StoreModule,
|
StoreModule,
|
||||||
} from '@ngrx/store';
|
} from '@ngrx/store';
|
||||||
|
import { createMockStore } from '@ngrx/store/testing';
|
||||||
import {
|
import {
|
||||||
TranslateLoader,
|
TranslateLoader,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
@@ -104,7 +105,7 @@ describe('GroupDataService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
init();
|
init();
|
||||||
requestService = getMockRequestService(createRequestEntry$(groups));
|
requestService = getMockRequestService(createRequestEntry$(groups));
|
||||||
store = new Store<CoreState>(undefined, undefined, undefined);
|
store = createMockStore({});
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
spyOn(store, 'dispatch');
|
spyOn(store, 'dispatch');
|
||||||
spyOn(rdbService, 'buildFromRequestUUIDAndAwait').and.callThrough();
|
spyOn(rdbService, 'buildFromRequestUUIDAndAwait').and.callThrough();
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
CanActivate,
|
CanActivateFn,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
UrlTree,
|
UrlTree,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
@@ -11,16 +11,13 @@ import { AuthorizationDataService } from '../data/feature-authorization/authoriz
|
|||||||
import { FeatureID } from '../data/feature-authorization/feature-id';
|
import { FeatureID } from '../data/feature-authorization/feature-id';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An guard for redirecting users to the feedback page if user is authorized
|
* A guard for redirecting users to the feedback page if user is authorized
|
||||||
*/
|
*/
|
||||||
@Injectable({ providedIn: 'root' })
|
export const feedbackGuard: CanActivateFn = (
|
||||||
export class FeedbackGuard implements CanActivate {
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
authorizationService: AuthorizationDataService = inject(AuthorizationDataService),
|
||||||
|
): Observable<boolean | UrlTree> => {
|
||||||
|
return authorizationService.isAuthorized(FeatureID.CanSendFeedback);
|
||||||
|
};
|
||||||
|
|
||||||
constructor(private authorizationService: AuthorizationDataService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
|
|
||||||
return this.authorizationService.isAuthorized(FeatureID.CanSendFeedback);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
50
src/app/core/lazy-data-service.ts
Normal file
50
src/app/core/lazy-data-service.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
Injector,
|
||||||
|
Type,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
defer,
|
||||||
|
Observable,
|
||||||
|
} from 'rxjs';
|
||||||
|
|
||||||
|
import { LazyDataServicesMap } from '../../config/app-config.interface';
|
||||||
|
import { HALDataService } from './data/base/hal-data-service.interface';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a service lazily. The service is loaded when the observable is subscribed to.
|
||||||
|
*
|
||||||
|
* @param dataServicesMap A map of promises returning the data services to load
|
||||||
|
* @param key The key of the service
|
||||||
|
* @param injector The injector to use to load the service. If not provided, the current injector is used.
|
||||||
|
* @returns An observable of the service.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const dataService$ = lazyDataService({ 'data-service': () => import('./data-service').then((m) => m.MyService)}, 'data-service', this.injector);
|
||||||
|
* or
|
||||||
|
* const dataService$ = lazyDataService({'data-service': () => import('./data-service')}, 'data-service', this.injector);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function lazyDataService<T>(
|
||||||
|
dataServicesMap: LazyDataServicesMap,
|
||||||
|
key: string,
|
||||||
|
injector: Injector,
|
||||||
|
): Observable<T> {
|
||||||
|
return defer(() => {
|
||||||
|
if (dataServicesMap.has(key) && typeof dataServicesMap.get(key) === 'function') {
|
||||||
|
const loader: () => Promise<Type<HALDataService<any>> | { default: HALDataService<any> }> = dataServicesMap.get(key);
|
||||||
|
return loader()
|
||||||
|
.then((serviceOrDefault) => {
|
||||||
|
if ('default' in serviceOrDefault) {
|
||||||
|
return injector!.get(serviceOrDefault.default);
|
||||||
|
}
|
||||||
|
return injector!.get(serviceOrDefault);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@@ -1,40 +0,0 @@
|
|||||||
import {
|
|
||||||
Injector,
|
|
||||||
Type,
|
|
||||||
} from '@angular/core';
|
|
||||||
import {
|
|
||||||
defer,
|
|
||||||
Observable,
|
|
||||||
} from 'rxjs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a service lazily. The service is loaded when the observable is subscribed to.
|
|
||||||
*
|
|
||||||
* @param loader A function that returns a promise of the service to load.
|
|
||||||
* @param injector The injector to use to load the service. If not provided, the current injector is used.
|
|
||||||
* @returns An observable of the service.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* const dataService$ = lazyService(() => import('./data-service').then((m) => m.MyService), this.injector);
|
|
||||||
* or
|
|
||||||
* const dataService$ = lazyService(() => import('./data-service'), this.injector);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export function lazyService<T>(
|
|
||||||
loader: () => Promise<Type<T>> | Promise<{ default: Type<T> }>,
|
|
||||||
injector: Injector,
|
|
||||||
): Observable<T> {
|
|
||||||
return defer(() => {
|
|
||||||
return loader()
|
|
||||||
.then((serviceOrDefault) => {
|
|
||||||
if ('default' in serviceOrDefault) {
|
|
||||||
return injector!.get(serviceOrDefault.default);
|
|
||||||
}
|
|
||||||
return injector!.get(serviceOrDefault);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@@ -10,7 +10,7 @@ import {
|
|||||||
NavigationEnd,
|
NavigationEnd,
|
||||||
Router,
|
Router,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { getMockStore } from '@ngrx/store/testing';
|
import { createMockStore } from '@ngrx/store/testing';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
Observable,
|
Observable,
|
||||||
@@ -106,8 +106,7 @@ describe('MetadataService', () => {
|
|||||||
isAuthorized: observableOf(true),
|
isAuthorized: observableOf(true),
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
store = createMockStore({ initialState });
|
||||||
store = getMockStore({ initialState });
|
|
||||||
spyOn(store, 'dispatch');
|
spyOn(store, 'dispatch');
|
||||||
|
|
||||||
appConfig = {
|
appConfig = {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user