mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge remote-tracking branch 'lyrasis/main' into task/main/CST-18964
# Conflicts: # package-lock.json # package.json
This commit is contained in:
@@ -35,6 +35,19 @@ ssr:
|
|||||||
# If set to true the component will be included in the HTML returned from the server side rendering.
|
# If set to true the component will be included in the HTML returned from the server side rendering.
|
||||||
# If set to false the component will not be included in the HTML returned from the server side rendering.
|
# If set to false the component will not be included in the HTML returned from the server side rendering.
|
||||||
enableBrowseComponent: false
|
enableBrowseComponent: false
|
||||||
|
# Enable state transfer from the server-side application to the client-side application.
|
||||||
|
# Defaults to true.
|
||||||
|
# Note: When using an external application cache layer, it's recommended not to transfer the state to avoid caching it.
|
||||||
|
# Disabling it ensures that dynamic state information is not inadvertently cached, which can improve security and
|
||||||
|
# ensure that users always use the most up-to-date state.
|
||||||
|
transferState: true
|
||||||
|
# When a different REST base URL is used for the server-side application, the generated state contains references to
|
||||||
|
# REST resources with the internal URL configured. By default, these internal URLs are replaced with public URLs.
|
||||||
|
# Disable this setting to avoid URL replacement during SSR. In this the state is not transferred to avoid security issues.
|
||||||
|
replaceRestUrl: true
|
||||||
|
# Enable request performance profiling data collection and printing the results in the server console.
|
||||||
|
# Defaults to false. Enabling in production is NOT recommended
|
||||||
|
#enablePerformanceProfiler: false
|
||||||
|
|
||||||
# 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
|
||||||
@@ -45,6 +58,9 @@ rest:
|
|||||||
port: 443
|
port: 443
|
||||||
# NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
|
# NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
|
||||||
nameSpace: /server
|
nameSpace: /server
|
||||||
|
# Provide a different REST url to be used during SSR execution. It must contain the whole url including protocol, server port and
|
||||||
|
# server namespace (uncomment to use it).
|
||||||
|
#ssrBaseUrl: http://localhost:8080/server
|
||||||
|
|
||||||
# Caching settings
|
# Caching settings
|
||||||
cache:
|
cache:
|
||||||
|
@@ -12,6 +12,13 @@ describe('Community List Page', () => {
|
|||||||
cy.get('[data-test="expand-button"]').click({ multiple: true });
|
cy.get('[data-test="expand-button"]').click({ multiple: true });
|
||||||
|
|
||||||
// Analyze <ds-community-list-page> for accessibility issues
|
// Analyze <ds-community-list-page> for accessibility issues
|
||||||
testA11y('ds-community-list-page');
|
testA11y('ds-community-list-page', {
|
||||||
|
rules: {
|
||||||
|
// When expanding a cdk node on the community-list page, the 'aria-posinset' property becomes 0.
|
||||||
|
// 0 is not a valid value for 'aria-posinset' so the test fails.
|
||||||
|
// see https://github.com/DSpace/dspace-angular/issues/4068
|
||||||
|
'aria-valid-attr-value': { enabled: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -217,7 +217,7 @@ describe('New Submission page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Close popup window
|
// Close popup window
|
||||||
cy.get('ds-dynamic-lookup-relation-modal button.close').click();
|
cy.get('ds-dynamic-lookup-relation-modal button.btn-close').click();
|
||||||
|
|
||||||
// Back on the form, click the discard button to remove new submission
|
// Back on the form, click the discard button to remove new submission
|
||||||
// Clicking it will display a confirmation, which we will confirm with another click
|
// Clicking it will display a confirmation, which we will confirm with another click
|
||||||
|
11600
package-lock.json
generated
11600
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
124
package.json
124
package.json
@@ -57,70 +57,66 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@kolkov/ngx-gallery": {
|
"@kolkov/ngx-gallery": {
|
||||||
"@angular/animations": "^17.3.11",
|
"@angular/animations": "^18.2.12",
|
||||||
"@angular/common": "^17.3.11",
|
"@angular/common": "^18.2.12",
|
||||||
"@angular/core": "^17.3.11"
|
"@angular/core": "^18.2.12"
|
||||||
},
|
},
|
||||||
"@ng-bootstrap/ng-bootstrap": {
|
"@ng-bootstrap/ng-bootstrap": {
|
||||||
"@angular/common": "^17.3.11",
|
"@angular/common": "^18.2.12",
|
||||||
"@angular/core": "^17.3.11",
|
"@angular/core": "^18.2.12",
|
||||||
"@angular/forms": "^17.3.11",
|
"@angular/forms": "^18.2.12",
|
||||||
"@angular/localize": "^17.3.11"
|
"@angular/localize": "^18.2.12"
|
||||||
},
|
},
|
||||||
"@ng-dynamic-forms/core": {
|
"@ng-dynamic-forms/core": {
|
||||||
"@angular/common": "^17.3.11",
|
"@angular/common": "^18.2.12",
|
||||||
"@angular/core": "^17.3.11",
|
"@angular/core": "^18.2.12",
|
||||||
"@angular/forms": "^17.3.11"
|
"@angular/forms": "^18.2.12"
|
||||||
},
|
},
|
||||||
"@ng-dynamic-forms/ui-ng-bootstrap": {
|
"@ng-dynamic-forms/ui-ng-bootstrap": {
|
||||||
"ngx-mask": "14.2.4"
|
"ngx-mask": "14.2.4",
|
||||||
},
|
"@ng-bootstrap/ng-bootstrap": "^12.0.0",
|
||||||
"@ngtools/webpack": {
|
"bootstrap": "^5.3"
|
||||||
"@angular/compiler-cli": "^17.3.11",
|
|
||||||
"typescript": "~5.4.5"
|
|
||||||
},
|
},
|
||||||
"@nicky-lenaers/ngx-scroll-to": {
|
"@nicky-lenaers/ngx-scroll-to": {
|
||||||
"@angular/common": "^17.3.11",
|
"@angular/common": "^18.2.12",
|
||||||
"@angular/core": "^17.3.11"
|
"@angular/core": "^18.2.12"
|
||||||
},
|
},
|
||||||
"eslint-plugin-unused-imports": {
|
"eslint-plugin-unused-imports": {
|
||||||
"@typescript-eslint/eslint-plugin": "^7.2.0"
|
"@typescript-eslint/eslint-plugin": "^7.2.0"
|
||||||
},
|
},
|
||||||
"ng2-file-upload": {
|
|
||||||
"@angular/common": "^17.3.11",
|
|
||||||
"@angular/core": "^17.3.11"
|
|
||||||
},
|
|
||||||
"ngx-infinite-scroll": {
|
"ngx-infinite-scroll": {
|
||||||
"@angular/common": "^17.3.11",
|
"@angular/common": "^18.2.12",
|
||||||
"@angular/core": "^17.3.11"
|
"@angular/core": "^18.2.12"
|
||||||
}
|
},
|
||||||
|
"notistack": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^17.3.12",
|
"@angular/animations": "^18.2.12",
|
||||||
"@angular/cdk": "^17.3.10",
|
"@angular/cdk": "^18.2.12",
|
||||||
"@angular/common": "^17.3.12",
|
"@angular/common": "^18.2.12",
|
||||||
"@angular/compiler": "^17.3.12",
|
"@angular/compiler": "^18.2.12",
|
||||||
"@angular/core": "^17.3.12",
|
"@angular/core": "^18.2.12",
|
||||||
"@angular/forms": "^17.3.12",
|
"@angular/forms": "^18.2.12",
|
||||||
"@angular/localize": "^17.3.12",
|
"@angular/localize": "^18.2.12",
|
||||||
"@angular/platform-browser": "^17.3.12",
|
"@angular/platform-browser": "^18.2.12",
|
||||||
"@angular/platform-browser-dynamic": "^17.3.12",
|
"@angular/platform-browser-dynamic": "^18.2.12",
|
||||||
"@angular/platform-server": "^17.3.12",
|
"@angular/platform-server": "^18.2.12",
|
||||||
"@angular/router": "^17.3.12",
|
"@angular/router": "^18.2.12",
|
||||||
"@angular/ssr": "^17.3.11",
|
"@angular/ssr": "^18.2.12",
|
||||||
"@babel/runtime": "7.26.0",
|
"@babel/runtime": "7.26.0",
|
||||||
"@kolkov/ngx-gallery": "^2.0.1",
|
"@kolkov/ngx-gallery": "^2.0.1",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^12.0.0",
|
||||||
"@ng-dynamic-forms/core": "^16.0.0",
|
"@ng-dynamic-forms/core": "^16.0.0",
|
||||||
"@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0",
|
"@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0",
|
||||||
"@ngrx/effects": "^17.1.1",
|
"@ngrx/effects": "^18.1.1",
|
||||||
"@ngrx/router-store": "^17.1.1",
|
"@ngrx/operators": "^18.0.0",
|
||||||
"@ngrx/store": "^17.1.1",
|
"@ngrx/router-store": "^18.1.1",
|
||||||
"@ngx-translate/core": "^14.0.0",
|
"@ngrx/store": "^18.1.1",
|
||||||
|
"@ngx-translate/core": "^16.0.3",
|
||||||
"@nicky-lenaers/ngx-scroll-to": "^14.0.0",
|
"@nicky-lenaers/ngx-scroll-to": "^14.0.0",
|
||||||
"angulartics2": "^12.2.0",
|
"angulartics2": "^12.2.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bootstrap": "^4.6.1",
|
"bootstrap": "^5.3",
|
||||||
"cerialize": "0.1.18",
|
"cerialize": "0.1.18",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
@@ -137,7 +133,7 @@
|
|||||||
"filesize": "^6.1.0",
|
"filesize": "^6.1.0",
|
||||||
"http-proxy-middleware": "^2.0.7",
|
"http-proxy-middleware": "^2.0.7",
|
||||||
"http-terminator": "^3.2.0",
|
"http-terminator": "^3.2.0",
|
||||||
"isbot": "^5.1.21",
|
"isbot": "^5.1.22",
|
||||||
"js-cookie": "2.2.1",
|
"js-cookie": "2.2.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
@@ -150,13 +146,13 @@
|
|||||||
"mirador-dl-plugin": "^0.13.0",
|
"mirador-dl-plugin": "^0.13.0",
|
||||||
"mirador-share-plugin": "^0.16.0",
|
"mirador-share-plugin": "^0.16.0",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ng2-file-upload": "5.0.0",
|
"ng2-file-upload": "7.0.1",
|
||||||
"ng2-nouislider": "^2.0.0",
|
"ng2-nouislider": "^2.0.0",
|
||||||
"ngx-infinite-scroll": "^16.0.0",
|
"ngx-infinite-scroll": "^18.0.0",
|
||||||
"ngx-matomo-client": "^6.4.1",
|
"ngx-matomo-client": "^6.4.1",
|
||||||
"ngx-pagination": "6.0.3",
|
"ngx-pagination": "6.0.3",
|
||||||
"ngx-skeleton-loader": "^9.0.0",
|
"ngx-skeleton-loader": "^9.0.0",
|
||||||
"ngx-ui-switch": "^14.1.0",
|
"ngx-ui-switch": "^15.0.0",
|
||||||
"nouislider": "^15.7.1",
|
"nouislider": "^15.7.1",
|
||||||
"orejime": "^2.3.1",
|
"orejime": "^2.3.1",
|
||||||
"pem": "1.14.8",
|
"pem": "1.14.8",
|
||||||
@@ -166,29 +162,29 @@
|
|||||||
"zone.js": "~0.14.10"
|
"zone.js": "~0.14.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "~17.0.2",
|
"@angular-builders/custom-webpack": "~18.0.0",
|
||||||
"@angular-devkit/build-angular": "^17.3.11",
|
"@angular-devkit/build-angular": "^18.2.12",
|
||||||
"@angular-eslint/builder": "^17.5.3",
|
"@angular-eslint/builder": "^18.4.1",
|
||||||
"@angular-eslint/bundled-angular-compiler": "^17.5.3",
|
"@angular-eslint/bundled-angular-compiler": "^18.4.1",
|
||||||
"@angular-eslint/eslint-plugin": "^17.5.3",
|
"@angular-eslint/eslint-plugin": "^18.4.1",
|
||||||
"@angular-eslint/eslint-plugin-template": "^17.5.3",
|
"@angular-eslint/eslint-plugin-template": "^18.4.1",
|
||||||
"@angular-eslint/schematics": "^17.5.3",
|
"@angular-eslint/schematics": "^18.4.1",
|
||||||
"@angular-eslint/template-parser": "^17.5.3",
|
"@angular-eslint/template-parser": "^18.4.1",
|
||||||
"@angular-eslint/utils": "^17.5.3",
|
"@angular-eslint/utils": "^18.4.1",
|
||||||
"@angular/cli": "^17.3.11",
|
"@angular/cli": "^18.2.12",
|
||||||
"@angular/compiler-cli": "^17.3.11",
|
"@angular/compiler-cli": "^18.2.12",
|
||||||
"@angular/language-service": "^17.3.12",
|
"@angular/language-service": "^18.2.12",
|
||||||
"@cypress/schematic": "^1.5.0",
|
"@cypress/schematic": "^1.5.0",
|
||||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||||
"@ngrx/store-devtools": "^17.1.1",
|
"@ngrx/store-devtools": "^18.1.1",
|
||||||
"@ngtools/webpack": "^16.2.16",
|
"@ngtools/webpack": "^18.2.12",
|
||||||
"@types/deep-freeze": "0.1.5",
|
"@types/deep-freeze": "0.1.5",
|
||||||
"@types/ejs": "^3.1.2",
|
"@types/ejs": "^3.1.2",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/grecaptcha": "^3.0.9",
|
"@types/grecaptcha": "^3.0.9",
|
||||||
"@types/jasmine": "~3.6.0",
|
"@types/jasmine": "~3.6.0",
|
||||||
"@types/js-cookie": "2.2.6",
|
"@types/js-cookie": "2.2.6",
|
||||||
"@types/lodash": "^4.17.14",
|
"@types/lodash": "^4.17.15",
|
||||||
"@types/node": "^14.14.9",
|
"@types/node": "^14.14.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||||
"@typescript-eslint/parser": "^7.18.0",
|
"@typescript-eslint/parser": "^7.18.0",
|
||||||
@@ -199,7 +195,7 @@
|
|||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cypress": "^13.17.0",
|
"cypress": "^13.17.0",
|
||||||
"cypress-axe": "^1.5.0",
|
"cypress-axe": "^1.6.0",
|
||||||
"deep-freeze": "0.0.1",
|
"deep-freeze": "0.0.1",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-plugin-deprecation": "^1.4.1",
|
"eslint-plugin-deprecation": "^1.4.1",
|
||||||
@@ -208,7 +204,7 @@
|
|||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-import-newlines": "^1.3.1",
|
"eslint-plugin-import-newlines": "^1.3.1",
|
||||||
"eslint-plugin-jsdoc": "^45.0.0",
|
"eslint-plugin-jsdoc": "^45.0.0",
|
||||||
"eslint-plugin-jsonc": "^2.18.2",
|
"eslint-plugin-jsonc": "^2.19.1",
|
||||||
"eslint-plugin-lodash": "^7.4.0",
|
"eslint-plugin-lodash": "^7.4.0",
|
||||||
"eslint-plugin-rxjs": "^5.0.3",
|
"eslint-plugin-rxjs": "^5.0.3",
|
||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||||
@@ -231,7 +227,7 @@
|
|||||||
"postcss-loader": "^4.0.3",
|
"postcss-loader": "^4.0.3",
|
||||||
"postcss-preset-env": "^7.4.2",
|
"postcss-preset-env": "^7.4.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sass": "~1.83.4",
|
"sass": "~1.84.0",
|
||||||
"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",
|
||||||
|
18
server.ts
18
server.ts
@@ -20,10 +20,10 @@ import 'reflect-metadata';
|
|||||||
|
|
||||||
/* eslint-disable import/no-namespace */
|
/* eslint-disable import/no-namespace */
|
||||||
import * as morgan from 'morgan';
|
import * as morgan from 'morgan';
|
||||||
import * as express from 'express';
|
import express from 'express';
|
||||||
import * as ejs from 'ejs';
|
import * as ejs from 'ejs';
|
||||||
import * as compression from 'compression';
|
import * as compression from 'compression';
|
||||||
import * as expressStaticGzip from 'express-static-gzip';
|
import expressStaticGzip from 'express-static-gzip';
|
||||||
/* eslint-enable import/no-namespace */
|
/* eslint-enable import/no-namespace */
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import LRU from 'lru-cache';
|
import LRU from 'lru-cache';
|
||||||
@@ -81,6 +81,9 @@ let anonymousCache: LRU<string, any>;
|
|||||||
// extend environment with app config for server
|
// extend environment with app config for server
|
||||||
extendEnvironmentWithAppConfig(environment, appConfig);
|
extendEnvironmentWithAppConfig(environment, appConfig);
|
||||||
|
|
||||||
|
// The REST server base URL
|
||||||
|
const REST_BASE_URL = environment.rest.ssrBaseUrl || environment.rest.baseUrl;
|
||||||
|
|
||||||
// The Express app is exported so that it can be used by serverless Functions.
|
// The Express app is exported so that it can be used by serverless Functions.
|
||||||
export function app() {
|
export function app() {
|
||||||
|
|
||||||
@@ -156,7 +159,7 @@ export function app() {
|
|||||||
* Proxy the sitemaps
|
* Proxy the sitemaps
|
||||||
*/
|
*/
|
||||||
router.use('/sitemap**', createProxyMiddleware({
|
router.use('/sitemap**', createProxyMiddleware({
|
||||||
target: `${environment.rest.baseUrl}/sitemaps`,
|
target: `${REST_BASE_URL}/sitemaps`,
|
||||||
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
}));
|
}));
|
||||||
@@ -165,7 +168,7 @@ export function app() {
|
|||||||
* Proxy the linksets
|
* Proxy the linksets
|
||||||
*/
|
*/
|
||||||
router.use('/signposting**', createProxyMiddleware({
|
router.use('/signposting**', createProxyMiddleware({
|
||||||
target: `${environment.rest.baseUrl}`,
|
target: `${REST_BASE_URL}`,
|
||||||
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
}));
|
}));
|
||||||
@@ -266,6 +269,11 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
|
|||||||
})
|
})
|
||||||
.then((html) => {
|
.then((html) => {
|
||||||
if (hasValue(html)) {
|
if (hasValue(html)) {
|
||||||
|
// Replace REST URL with UI URL
|
||||||
|
if (environment.ssr.replaceRestUrl && REST_BASE_URL !== environment.rest.baseUrl) {
|
||||||
|
html = html.replace(new RegExp(REST_BASE_URL, 'g'), environment.rest.baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
// save server side rendered page to cache (if any are enabled)
|
// save server side rendered page to cache (if any are enabled)
|
||||||
saveToCache(req, html);
|
saveToCache(req, html);
|
||||||
if (sendToUser) {
|
if (sendToUser) {
|
||||||
@@ -623,7 +631,7 @@ function start() {
|
|||||||
* The callback function to serve health check requests
|
* The callback function to serve health check requests
|
||||||
*/
|
*/
|
||||||
function healthCheck(req, res) {
|
function healthCheck(req, res) {
|
||||||
const baseUrl = `${environment.rest.baseUrl}${environment.actuators.endpointPath}`;
|
const baseUrl = `${REST_BASE_URL}${environment.actuators.endpointPath}`;
|
||||||
axios.get(baseUrl)
|
axios.get(baseUrl)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
res.status(response.status).send(response.data);
|
res.status(response.status).send(response.data);
|
||||||
|
@@ -1,19 +1,13 @@
|
|||||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'">
|
<ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'">
|
||||||
<ngb-panel [id]="'browse'">
|
<ngb-panel [id]="'browse'">
|
||||||
<ng-template ngbPanelHeader>
|
<ng-template ngbPanelTitle>
|
||||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')"
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="acc.toggle('browse')"
|
||||||
data-test="browse">
|
data-test="browse">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
|
||||||
[attr.aria-expanded]="acc.isExpanded('browse')"
|
[attr.aria-expanded]="acc.isExpanded('browse')"
|
||||||
aria-controls="bulk-access-browse-panel-content">
|
aria-controls="bulk-access-browse-panel-content">
|
||||||
{{ 'admin.access-control.bulk-access-browse.header' | translate }}
|
{{ 'admin.access-control.bulk-access-browse.header' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-right d-flex gap-2">
|
|
||||||
<div class="d-flex my-auto">
|
|
||||||
<span *ngIf="acc.isExpanded('browse')" class="fas fa-chevron-up fa-fw"></span>
|
|
||||||
<span *ngIf="!acc.isExpanded('browse')" class="fas fa-chevron-down fa-fw"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
@@ -22,7 +16,7 @@
|
|||||||
<li [ngbNavItem]="'search'" role="presentation">
|
<li [ngbNavItem]="'search'" role="presentation">
|
||||||
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
|
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="mx-n3">
|
<div class="bulk-access-search">
|
||||||
<ds-search [configuration]="'administrativeBulkAccess'"
|
<ds-search [configuration]="'administrativeBulkAccess'"
|
||||||
[selectable]="true"
|
[selectable]="true"
|
||||||
[selectionConfig]="{ repeatable: true, listId: listId }"
|
[selectionConfig]="{ repeatable: true, listId: listId }"
|
||||||
@@ -42,7 +36,7 @@
|
|||||||
[showPaginator]="false"
|
[showPaginator]="false"
|
||||||
(prev)="pagePrev()"
|
(prev)="pagePrev()"
|
||||||
(next)="pageNext()">
|
(next)="pageNext()">
|
||||||
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ml-4">
|
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ms-4">
|
||||||
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
|
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
|
||||||
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last '
|
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last '
|
||||||
class="mt-4 mb-4 d-flex"
|
class="mt-4 mb-4 d-flex"
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
.bulk-access-search {
|
||||||
|
margin-right: calc(var(--bs-gutter-x, 1.5rem) / -2);
|
||||||
|
margin-left: calc(var(--bs-gutter-x, 1.5rem) / -2);
|
||||||
|
}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<button class="btn btn-outline-primary mr-3" (click)="reset()">
|
<button class="btn btn-outline-primary me-3" (click)="reset()">
|
||||||
{{ 'access-control-cancel' | translate }}
|
{{ 'access-control-cancel' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" [dsBtnDisabled]="!canExport()" (click)="submit()">
|
<button class="btn btn-primary" [dsBtnDisabled]="!canExport()" (click)="submit()">
|
||||||
|
@@ -1,17 +1,11 @@
|
|||||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'">
|
<ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'">
|
||||||
<ngb-panel [id]="'settings'">
|
<ngb-panel [id]="'settings'">
|
||||||
<ng-template ngbPanelHeader>
|
<ng-template ngbPanelTitle>
|
||||||
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded('settings')"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded('settings')"
|
||||||
aria-controls="bulk-access-settings-panel-content">
|
aria-controls="bulk-access-settings-panel-content">
|
||||||
{{ 'admin.access-control.bulk-access-settings.header' | translate }}
|
{{ 'admin.access-control.bulk-access-settings.header' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-right d-flex gap-2">
|
|
||||||
<div class="d-flex my-auto">
|
|
||||||
<span *ngIf="acc.isExpanded('settings')" class="fas fa-chevron-up fa-fw"></span>
|
|
||||||
<span *ngIf="!acc.isExpanded('settings')" class="fas fa-chevron-down fa-fw"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
|
@@ -5,10 +5,10 @@
|
|||||||
<h1 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h1>
|
<h1 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h1>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="mr-auto btn btn-success addEPerson-button"
|
<button class="me-auto btn btn-success addEPerson-button"
|
||||||
[routerLink]="'create'">
|
[routerLink]="'create'">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
<span class="d-none d-sm-inline ml-1">{{labelPrefix + 'button.add' | translate}}</span>
|
<span class="d-none d-sm-inline ms-1">{{labelPrefix + 'button.add' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,13 +18,13 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
|
<select name="scope" id="scope" formControlName="scope" class="form-select" aria-label="Search scope">
|
||||||
<option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option>
|
<option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option>
|
||||||
<option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option>
|
<option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow-1 mr-3 ml-3">
|
<div class="flex-grow-1 me-3 ms-3">
|
||||||
<div class="form-group input-group">
|
<div class="mb-3 input-group">
|
||||||
<input type="text" name="query" id="query" formControlName="query"
|
<input type="text" name="query" id="query" formControlName="query"
|
||||||
class="form-control" [attr.aria-label]="labelPrefix + 'search.placeholder' | translate"
|
class="form-control" [attr.aria-label]="labelPrefix + 'search.placeholder' | translate"
|
||||||
[placeholder]="(labelPrefix + 'search.placeholder' | translate)">
|
[placeholder]="(labelPrefix + 'search.placeholder' | translate)">
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
<i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}}
|
<i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="canImpersonate$ | async" between class="btn-group">
|
<div *ngIf="canImpersonate$ | async" between class="btn-group ms-1">
|
||||||
<button *ngIf="!isImpersonated" class="btn btn-primary" type="button" (click)="impersonate()">
|
<button *ngIf="!isImpersonated" class="btn btn-primary" type="button" (click)="impersonate()">
|
||||||
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.impersonate' | translate}}
|
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.impersonate' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
@@ -75,8 +75,8 @@
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div class="flex-grow-1 mr-3">
|
<div class="flex-grow-1 me-3">
|
||||||
<div class="form-group input-group mr-3">
|
<div class="form-group input-group me-3">
|
||||||
<input type="text" name="query" id="query" formControlName="query"
|
<input type="text" name="query" id="query" formControlName="query"
|
||||||
class="form-control" aria-label="Search input">
|
class="form-control" aria-label="Search input">
|
||||||
<span class="input-group-append">
|
<span class="input-group-append">
|
||||||
|
@@ -62,8 +62,8 @@
|
|||||||
|
|
||||||
</h4>
|
</h4>
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div class="flex-grow-1 mr-3">
|
<div class="flex-grow-1 me-3">
|
||||||
<div class="form-group input-group mr-3">
|
<div class="mb-3 input-group me-3">
|
||||||
<input type="text" name="query" id="query" formControlName="query"
|
<input type="text" name="query" id="query" formControlName="query"
|
||||||
class="form-control" aria-label="Search input">
|
class="form-control" aria-label="Search input">
|
||||||
<span class="input-group-append">
|
<span class="input-group-append">
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button (click)="clearFormAndResetResult();" class="btn btn-secondary float-right">
|
<button (click)="clearFormAndResetResult();" class="btn btn-secondary float-end">
|
||||||
{{messagePrefix + '.button.see-all' | translate}}
|
{{messagePrefix + '.button.see-all' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,18 +4,18 @@
|
|||||||
<div class="d-flex justify-content-between border-bottom mb-3">
|
<div class="d-flex justify-content-between border-bottom mb-3">
|
||||||
<h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1>
|
<h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1>
|
||||||
<div>
|
<div>
|
||||||
<button class="mr-auto btn btn-success"
|
<button class="me-auto btn btn-success"
|
||||||
[routerLink]="'create'">
|
[routerLink]="'create'">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
<span class="d-none d-sm-inline ml-1">{{messagePrefix + 'button.add' | translate}}</span>
|
<span class="d-none d-sm-inline ms-1">{{messagePrefix + 'button.add' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h2>
|
<h2 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h2>
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div class="flex-grow-1 mr-3">
|
<div class="flex-grow-1 me-3">
|
||||||
<div class="form-group input-group">
|
<div class="mb-3 input-group">
|
||||||
<input type="text" name="query" id="query" formControlName="query"
|
<input type="text" name="query" id="query" formControlName="query"
|
||||||
class="form-control" [attr.aria-label]="messagePrefix + 'search.placeholder' | translate"
|
class="form-control" [attr.aria-label]="messagePrefix + 'search.placeholder' | translate"
|
||||||
[placeholder]="(messagePrefix + 'search.placeholder' | translate)" >
|
[placeholder]="(messagePrefix + 'search.placeholder' | translate)" >
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<button class="btn btn-primary" (click)="this.selectCollection();">{{'admin.metadata-import.page.button.select-collection' | translate}}</button>
|
<button class="btn btn-primary" (click)="this.selectCollection();">{{'admin.metadata-import.page.button.select-collection' | translate}}</button>
|
||||||
</p>
|
</p>
|
||||||
<div class="form-group">
|
<div class="mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" id="validateOnly" [(ngModel)]="validateOnly">
|
<input class="form-check-input" type="checkbox" id="validateOnly" [(ngModel)]="validateOnly">
|
||||||
<label class="form-check-label" for="validateOnly">
|
<label class="form-check-label" for="validateOnly">
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
[uncheckedLabel]="'admin.metadata-import.page.toggle.url' | translate"
|
[uncheckedLabel]="'admin.metadata-import.page.toggle.url' | translate"
|
||||||
[checked]="isUpload"
|
[checked]="isUpload"
|
||||||
(change)="toggleUpload()" ></ui-switch>
|
(change)="toggleUpload()" ></ui-switch>
|
||||||
<small class="form-text text-muted">
|
<small class="form-text text-muted d-block">
|
||||||
{{'admin.batch-import.page.toggle.help' | translate}}
|
{{'admin.batch-import.page.toggle.help' | translate}}
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
[dropMessageLabelReplacement]="'admin.batch-import.page.dropMsgReplace'">
|
[dropMessageLabelReplacement]="'admin.batch-import.page.dropMsgReplace'">
|
||||||
</ds-file-dropzone-no-uploader>
|
</ds-file-dropzone-no-uploader>
|
||||||
|
|
||||||
<div class="form-group mt-2" *ngIf="!isUpload">
|
<div class="mb-3 mt-2" *ngIf="!isUpload">
|
||||||
<input class="form-control" type="text" placeholder="{{'admin.metadata-import.page.urlMsg' | translate}}"
|
<input class="form-control" type="text" placeholder="{{'admin.metadata-import.page.urlMsg' | translate}}"
|
||||||
data-test="file-url-input" [(ngModel)]="fileURL">
|
data-test="file-url-input" [(ngModel)]="fileURL">
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 id="header">{{'admin.metadata-import.page.header' | translate}}</h1>
|
<h1 id="header">{{'admin.metadata-import.page.header' | translate}}</h1>
|
||||||
<p>{{'admin.metadata-import.page.help' | translate}}</p>
|
<p>{{'admin.metadata-import.page.help' | translate}}</p>
|
||||||
<div class="form-group">
|
<div class="mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" id="validateOnly" [(ngModel)]="validateOnly">
|
<input class="form-check-input" type="checkbox" id="validateOnly" [(ngModel)]="validateOnly">
|
||||||
<label class="form-check-label" for="validateOnly">
|
<label class="form-check-label" for="validateOnly">
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
<div class="mb-5 mt-5">
|
<div class="mb-5 mt-5">
|
||||||
<!-- In the url section -->
|
<!-- In the url section -->
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="d-flex flex-column w-50 mr-2">
|
<div class="d-flex flex-column w-50 me-2">
|
||||||
<label for="url" class="font-weight-bold">{{ 'ldn-new-service.form.label.url' | translate }}</label>
|
<label for="url" class="font-weight-bold">{{ 'ldn-new-service.form.label.url' | translate }}</label>
|
||||||
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
|
<input [class.invalid-field]="formModel.get('url').invalid && formModel.get('url').touched"
|
||||||
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" class="form-control"
|
[placeholder]="'ldn-new-service.form.placeholder.url' | translate" class="form-control"
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<label for="lowerIp" class="font-weight-bold">{{ 'ldn-new-service.form.label.ip-range' | translate }}</label>
|
<label for="lowerIp" class="font-weight-bold">{{ 'ldn-new-service.form.label.ip-range' | translate }}</label>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<input [class.invalid-field]="formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched"
|
<input [class.invalid-field]="formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched"
|
||||||
[placeholder]="'ldn-new-service.form.placeholder.lowerIp' | translate" class="form-control mr-2"
|
[placeholder]="'ldn-new-service.form.placeholder.lowerIp' | translate" class="form-control me-2"
|
||||||
formControlName="lowerIp"
|
formControlName="lowerIp"
|
||||||
id="lowerIp"
|
id="lowerIp"
|
||||||
name="lowerIp"
|
name="lowerIp"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<div *ngIf="!isNewService">
|
<div *ngIf="!isNewService">
|
||||||
<button (click)="closeModal()" class="btn btn-outline-secondary mr-2"
|
<button (click)="closeModal()" class="btn btn-outline-secondary me-2"
|
||||||
id="delete-confirm-edit">{{ 'service.detail.return' | translate }}
|
id="delete-confirm-edit">{{ 'service.detail.return' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="!isNewService" (click)="patchService()"
|
<button *ngIf="!isNewService" (click)="patchService()"
|
||||||
@@ -306,7 +306,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isNewService">
|
<div *ngIf="isNewService">
|
||||||
<button (click)="closeModal()" class="btn btn-outline-secondary mr-2 "
|
<button (click)="closeModal()" class="btn btn-outline-secondary me-2 "
|
||||||
id="delete-confirm-new">{{ 'service.refuse.create' | translate }}
|
id="delete-confirm-new">{{ 'service.refuse.create' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button (click)="createService()"
|
<button (click)="createService()"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<button class="btn btn-success" routerLink="/admin/ldn/services/new"><i
|
<button class="btn btn-success" routerLink="/admin/ldn/services/new"><i
|
||||||
class="fas fa-plus pr-2"></i>{{ 'process.overview.new' | translate }}</button>
|
class="fas fa-plus pe-2"></i>{{ 'process.overview.new' | translate }}</button>
|
||||||
</div>
|
</div>
|
||||||
<ds-pagination *ngIf="(ldnServicesRD$ | async)?.payload?.totalElements > 0"
|
<ds-pagination *ngIf="(ldnServicesRD$ | async)?.payload?.totalElements > 0"
|
||||||
[collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
|
[collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<div class="mt-4 text-right">
|
<div class="mt-4 text-right">
|
||||||
<button (click)="closeModal()"
|
<button (click)="closeModal()"
|
||||||
[attr.aria-label]="'ldn-service-overview-close-modal' | translate"
|
[attr.aria-label]="'ldn-service-overview-close-modal' | translate"
|
||||||
class="btn btn-outline-secondary mr-2">{{ 'service.detail.delete.cancel' | translate }}</button>
|
class="btn btn-outline-secondary me-2">{{ 'service.detail.delete.cancel' | translate }}</button>
|
||||||
<button (click)="deleteSelected(this.selectedServiceId.toString(), ldnServicesService)"
|
<button (click)="deleteSelected(this.selectedServiceId.toString(), ldnServicesService)"
|
||||||
class="btn btn-danger"
|
class="btn btn-danger"
|
||||||
[attr.aria-label]="'ldn-service-overview-select-delete' | translate"
|
[attr.aria-label]="'ldn-service-overview-select-delete' | translate"
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<div class="col-12 col-md-3 text-left h4">{{((isInbound$ | async) ? 'admin.notify.dashboard.inbound' : 'admin.notify.dashboard.outbound') | translate}}</div>
|
<div class="col-12 col-md-3 text-left h4">{{((isInbound$ | async) ? 'admin.notify.dashboard.inbound' : 'admin.notify.dashboard.outbound') | translate}}</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<div class="h4">
|
<div class="h4">
|
||||||
<button (click)="resetDefaultConfiguration()" *ngIf="(selectedSearchConfig$ | async) !== defaultConfiguration" class="badge badge-primary mr-1 mb-1">
|
<button (click)="resetDefaultConfiguration()" *ngIf="(selectedSearchConfig$ | async) !== defaultConfiguration" class="badge bg-primary me-1 mb-1">
|
||||||
{{ 'admin-notify-logs.' + (selectedSearchConfig$ | async) | translate}}
|
{{ 'admin-notify-logs.' + (selectedSearchConfig$ | async) | translate}}
|
||||||
<span> ×</span>
|
<span> ×</span>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let bitstreamFormat of (bitstreamFormats$ | async)?.payload?.page">
|
<tr *ngFor="let bitstreamFormat of (bitstreamFormats$ | async)?.payload?.page">
|
||||||
<td>
|
<td>
|
||||||
<label class="mb-0">
|
<label class="form-label mb-0">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
[attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"
|
[attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"
|
||||||
[checked]="(selectedBitstreamFormatIDs$ | async)?.includes(bitstreamFormat.id)"
|
[checked]="(selectedBitstreamFormatIDs$ | async)?.includes(bitstreamFormat.id)"
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button *ngIf="(bitstreamFormats$ | async)?.payload?.page?.length > 0" class="btn btn-primary deselect" (click)="deselectAll()">{{'admin.registries.bitstream-formats.table.deselect-all' | translate}}</button>
|
<button *ngIf="(bitstreamFormats$ | async)?.payload?.page?.length > 0" class="btn btn-primary deselect" (click)="deselectAll()">{{'admin.registries.bitstream-formats.table.deselect-all' | translate}}</button>
|
||||||
<button *ngIf="(bitstreamFormats$ | async)?.payload?.page?.length > 0" type="submit" class="btn btn-danger float-right" (click)="deleteFormats()">{{'admin.registries.bitstream-formats.table.delete' | translate}}</button>
|
<button *ngIf="(bitstreamFormats$ | async)?.payload?.page?.length > 0" type="submit" class="btn btn-danger float-end" (click)="deleteFormats()">{{'admin.registries.bitstream-formats.table.delete' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -64,7 +64,7 @@ export class FormatFormComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
arrayElementLayout: DynamicFormControlLayout = {
|
arrayElementLayout: DynamicFormControlLayout = {
|
||||||
grid: {
|
grid: {
|
||||||
group: 'form-row',
|
group: 'row',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
<tr *ngFor="let schema of (metadataSchemas | async)?.payload?.page"
|
<tr *ngFor="let schema of (metadataSchemas | async)?.payload?.page"
|
||||||
[ngClass]="{'table-primary' : (activeMetadataSchema$ | async)?.id === schema.id}">
|
[ngClass]="{'table-primary' : (activeMetadataSchema$ | async)?.id === schema.id}">
|
||||||
<td>
|
<td>
|
||||||
<label class="mb-0">
|
<label class="form-label mb-0">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
[checked]="(selectedMetadataSchemaIDs$ | async)?.includes(schema.id)"
|
[checked]="(selectedMetadataSchemaIDs$ | async)?.includes(schema.id)"
|
||||||
(change)="selectMetadataSchema(schema, $event)"
|
(change)="selectMetadataSchema(schema, $event)"
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button *ngIf="(metadataSchemas | async)?.payload?.page?.length > 0" type="submit" class="btn btn-danger float-right" (click)="deleteSchemas()">{{'admin.registries.metadata.schemas.table.delete' | translate}}</button>
|
<button *ngIf="(metadataSchemas | async)?.payload?.page?.length > 0" type="submit" class="btn btn-danger float-end" (click)="deleteSchemas()">{{'admin.registries.metadata.schemas.table.delete' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button [routerLink]="['/admin/registries/metadata']" class="btn btn-primary">{{'admin.registries.schema.return' | translate}}</button>
|
<button [routerLink]="['/admin/registries/metadata']" class="btn btn-primary">{{'admin.registries.schema.return' | translate}}</button>
|
||||||
<button *ngIf="fields?.page?.length > 0" type="submit" class="btn btn-danger float-right" (click)="deleteFields()">{{'admin.registries.schema.fields.table.delete' | translate}}</button>
|
<button *ngIf="fields?.page?.length > 0" type="submit" class="btn btn-danger float-end" (click)="deleteFields()">{{'admin.registries.schema.fields.table.delete' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import {
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ComponentFixture,
|
ComponentFixture,
|
||||||
@@ -39,22 +43,21 @@ describe('FiltersComponent', () => {
|
|||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
NgbAccordionModule,
|
imports: [NgbAccordionModule,
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useClass: TranslateLoaderMock,
|
useClass: TranslateLoaderMock,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
HttpClientTestingModule,
|
FilteredCollectionsComponent],
|
||||||
FilteredCollectionsComponent,
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
DspaceRestService,
|
DspaceRestService,
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
<legend>{{group.key | translate}}</legend>
|
<legend>{{group.key | translate}}</legend>
|
||||||
<ng-container [formGroup]="filtersForm">
|
<ng-container [formGroup]="filtersForm">
|
||||||
<div *ngFor="let filter of group.filters" class="col-6">
|
<div *ngFor="let filter of group.filters" class="col-6">
|
||||||
<input type="checkbox" id="flt-{{filter.id}}" value="{{filter.id}}" class="form-check-input col-1 align-baseline" formControlName="{{filter.id}}"><label for="flt-{{filter.id}}" class="col-11 align-middle" title="{{filter.tooltipKey | translate}}">{{filter.key | translate}}</label>
|
<input type="checkbox" id="flt-{{filter.id}}" value="{{filter.id}}" class="form-check-input col-1" formControlName="{{filter.id}}"><label for="flt-{{filter.id}}" class="col-11 align-middle" title="{{filter.tooltipKey | translate}}">{{filter.key | translate}}</label>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
.col-6 > label.col-11.align-middle {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.row-cols-2 > legend {
|
||||||
|
float: none !important;
|
||||||
|
}
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="modal-header">{{'supervision-group-selector.header' | translate}}
|
<div class="modal-header">{{'supervision-group-selector.header' | translate}}
|
||||||
<button type="button" class="close" (click)="close()" aria-label="Close">
|
<button type="button" class="btn-close" (click)="close()" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="control-group col-sm-12">
|
<div class="control-group col-sm-12">
|
||||||
<label for="supervisionOrder">{{'supervision-group-selector.select.type-of-order.label' | translate}}</label>
|
<label for="supervisionOrder" class="form-label">{{'supervision-group-selector.select.type-of-order.label' | translate}}</label>
|
||||||
<select name="supervisionOrder" id="supervisionOrder" class="form-control"
|
<select name="supervisionOrder" id="supervisionOrder" class="form-select"
|
||||||
[(ngModel)]="selectedOrderType"
|
[(ngModel)]="selectedOrderType"
|
||||||
attr.aria-label="{{'supervision-group-selector.select.type-of-order.label' | translate}}">
|
attr.aria-label="{{'supervision-group-selector.select.type-of-order.label' | translate}}">
|
||||||
<option value="EDITOR">{{'supervision-group-selector.select.type-of-order.option.editor' | translate}}</option>
|
<option value="EDITOR">{{'supervision-group-selector.select.type-of-order.option.editor' | translate}}</option>
|
||||||
@@ -20,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="control-group col-sm-12">
|
<div class="control-group col-sm-12">
|
||||||
<label for="supervisionGroup">{{'supervision-group-selector.select.group.label' | translate}}</label>
|
<label for="supervisionGroup" class="form-label">{{'supervision-group-selector.select.group.label' | translate}}</label>
|
||||||
<ng-container class="mb-3">
|
<ng-container class="mb-3">
|
||||||
<input id="supervisionGroup" class="form-control" type="text" [value]="selectedGroup ? dsoNameService.getName(selectedGroup) : ''" disabled>
|
<input id="supervisionGroup" class="form-control" type="text" [value]="selectedGroup ? dsoNameService.getName(selectedGroup) : ''" disabled>
|
||||||
<ds-error *ngIf="isSubmitted && !selectedGroup" message="{{'supervision-group-selector.select.group.error' | translate}}"></ds-error>
|
<ds-error *ngIf="isSubmitted && !selectedGroup" message="{{'supervision-group-selector.select.group.error' | translate}}"></ds-error>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<span>{{'workflow-item.search.result.list.element.supervised-by' | translate}} </span>
|
<span>{{'workflow-item.search.result.list.element.supervised-by' | translate}} </span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a class="badge badge-primary mr-1 mb-1 text-capitalize mw-100 text-truncate" *ngFor="let supervisionOrder of supervisionOrders" data-test="soBadge"
|
<a class="badge bg-primary me-1 mb-1 text-capitalize mw-100 text-truncate" *ngFor="let supervisionOrder of supervisionOrders" data-test="soBadge"
|
||||||
[ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate"
|
[ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate"
|
||||||
(click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close">
|
(click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close">
|
||||||
{{ dsoNameService.getName(supervisionOrder.group) }}
|
{{ dsoNameService.getName(supervisionOrder.group) }}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<ng-template dsDynamicComponentLoader>
|
<ng-template dsDynamicComponentLoader>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<div #badges class="position-absolute ml-1">
|
<div #badges class="position-absolute ms-1">
|
||||||
<div class="workflow-badge">
|
<div class="workflow-badge">
|
||||||
<span class="badge badge-info">{{ "admin.workflow.item.workflow" | translate }}</span>
|
<span class="badge bg-info">{{ "admin.workflow.item.workflow" | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul #buttons class="list-group list-group-flush">
|
<ul #buttons class="list-group list-group-flush">
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<ng-template dsDynamicComponentLoader>
|
<ng-template dsDynamicComponentLoader>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<div #badges class="position-absolute ml-1">
|
<div #badges class="position-absolute ms-1">
|
||||||
<div class="workflow-badge">
|
<div class="workflow-badge">
|
||||||
<span class="badge badge-info">{{ "admin.workflow.item.workspace" | translate }}</span>
|
<span class="badge bg-info">{{ "admin.workflow.item.workspace" | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul #buttons class="list-group list-group-flush">
|
<ul #buttons class="list-group list-group-flush">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="workflow-badge">
|
<div class="workflow-badge">
|
||||||
<span class="badge badge-info">{{ "admin.workflow.item.workflow" | translate }}</span>
|
<span class="badge bg-info">{{ "admin.workflow.item.workflow" | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ds-listable-object-component-loader *ngIf="item$ | async"
|
<ds-listable-object-component-loader *ngIf="item$ | async"
|
||||||
[object]="item$ | async"
|
[object]="item$ | async"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<span class="badge badge-info">{{ "admin.workflow.item.workspace" | translate }}</span>
|
<span class="badge bg-info">{{ "admin.workflow.item.workspace" | translate }}</span>
|
||||||
<ds-listable-object-component-loader *ngIf="item$ | async"
|
<ds-listable-object-component-loader *ngIf="item$ | async"
|
||||||
[object]="item$ | async"
|
[object]="item$ | async"
|
||||||
[viewMode]="viewModes.ListElement"
|
[viewMode]="viewModes.ListElement"
|
||||||
|
@@ -108,7 +108,7 @@ export const APP_ROUTES: Route[] = [
|
|||||||
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),
|
||||||
data: { showBreadcrumbs: false, enableRSS: true },
|
data: { enableRSS: true },
|
||||||
canActivate: [endUserAgreementCurrentUserGuard],
|
canActivate: [endUserAgreementCurrentUserGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -54,6 +54,7 @@ import {
|
|||||||
} from './app-routes';
|
} from './app-routes';
|
||||||
import { BROWSE_BY_DECORATOR_MAP } from './browse-by/browse-by-switcher/browse-by-decorator';
|
import { BROWSE_BY_DECORATOR_MAP } from './browse-by/browse-by-switcher/browse-by-decorator';
|
||||||
import { AuthInterceptor } from './core/auth/auth.interceptor';
|
import { AuthInterceptor } from './core/auth/auth.interceptor';
|
||||||
|
import { DspaceRestInterceptor } from './core/dspace-rest/dspace-rest.interceptor';
|
||||||
import { LocaleInterceptor } from './core/locale/locale.interceptor';
|
import { LocaleInterceptor } from './core/locale/locale.interceptor';
|
||||||
import { LogInterceptor } from './core/log/log.interceptor';
|
import { LogInterceptor } from './core/log/log.interceptor';
|
||||||
import {
|
import {
|
||||||
@@ -148,6 +149,11 @@ export const commonAppConfig: ApplicationConfig = {
|
|||||||
useClass: LogInterceptor,
|
useClass: LogInterceptor,
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: DspaceRestInterceptor,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
// 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,
|
||||||
provideCore(),
|
provideCore(),
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<ds-resource-policies [resourceType]="'bitstream'" [resourceUUID]="(dsoRD$ | async)?.payload?.id"></ds-resource-policies>
|
<ds-resource-policies [resourceType]="'bitstream'" [resourceUUID]="(dsoRD$ | async)?.payload?.id"></ds-resource-policies>
|
||||||
<div class="button-row bottom">
|
<div class="button-row bottom">
|
||||||
<div class="text-right">
|
<div class="text-end">
|
||||||
<a [routerLink]="['/bitstreams', (dsoRD$ | async)?.payload?.id, 'edit']" role="button" class="btn btn-outline-secondary mr-1">
|
<a [routerLink]="['/bitstreams', (dsoRD$ | async)?.payload?.id, 'edit']" role="button" class="btn btn-outline-secondary me-1">
|
||||||
<i class="fas fa-arrow-left"></i> {{'bitstream.edit.return' | translate}}
|
<i class="fas fa-arrow-left"></i> {{'bitstream.edit.return' | translate}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -255,7 +255,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
|
|||||||
group: [this.iiifLabelModel],
|
group: [this.iiifLabelModel],
|
||||||
}, {
|
}, {
|
||||||
grid: {
|
grid: {
|
||||||
host: 'form-row',
|
host: 'row',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
|
|||||||
group: [this.iiifTocModel],
|
group: [this.iiifTocModel],
|
||||||
}, {
|
}, {
|
||||||
grid: {
|
grid: {
|
||||||
host: 'form-row',
|
host: 'row',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
|
|||||||
group: [this.iiifWidthModel],
|
group: [this.iiifWidthModel],
|
||||||
}, {
|
}, {
|
||||||
grid: {
|
grid: {
|
||||||
host: 'form-row',
|
host: 'row',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -309,7 +309,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
|
|||||||
group: [this.iiifHeightModel],
|
group: [this.iiifHeightModel],
|
||||||
}, {
|
}, {
|
||||||
grid: {
|
grid: {
|
||||||
host: 'form-row',
|
host: 'row',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
|
|||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
grid: {
|
grid: {
|
||||||
host: 'form-row',
|
host: 'row',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new DynamicFormGroupModel({
|
new DynamicFormGroupModel({
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
padding-bottom: calc(var(--ds-content-spacing) / 2);
|
padding-bottom: calc(var(--ds-content-spacing) / 2);
|
||||||
padding-top: calc(var(--ds-content-spacing) / 2);
|
padding-top: calc(var(--ds-content-spacing) / 2);
|
||||||
background-color: var(--ds-breadcrumb-bg);
|
background-color: var(--ds-breadcrumb-bg);
|
||||||
|
padding-left: calc(var(--bs-spacer) *.75);
|
||||||
|
padding-right: calc(var(--bs-spacer) *.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
li.breadcrumb-item {
|
li.breadcrumb-item {
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<div *ngIf="collectionRD?.payload as collection">
|
<div *ngIf="collectionRD?.payload as collection">
|
||||||
<ds-view-tracker [object]="collection"></ds-view-tracker>
|
<ds-view-tracker [object]="collection"></ds-view-tracker>
|
||||||
<div class="d-flex flex-row border-bottom mb-4 pb-4">
|
<div class="d-flex flex-row border-bottom mb-4 pb-4">
|
||||||
<header class="comcol-header mr-auto">
|
<header class="comcol-header me-auto">
|
||||||
<!-- Collection Name -->
|
<!-- Collection Name -->
|
||||||
<ds-comcol-page-header
|
<ds-comcol-page-header
|
||||||
[name]="dsoNameService.getName(collection)">
|
[name]="dsoNameService.getName(collection)">
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
<div class="col-12 pb-4">
|
<div class="col-12 pb-4">
|
||||||
<h1 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h1>
|
<h1 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h1>
|
||||||
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
||||||
<div class="form-group row">
|
<div class="mb-3 row">
|
||||||
<div class="col text-right space-children-mr">
|
<div class="col text-end space-children-mr">
|
||||||
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)">
|
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)">
|
||||||
<i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}}
|
<i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
|
<div class="custom-alignment">
|
||||||
<ds-comcol-role
|
<ds-comcol-role
|
||||||
*ngFor="let comcolRole of comcolRoles$ | async"
|
*ngFor="let comcolRole of comcolRoles$ | async"
|
||||||
[dso]="collection$ | async"
|
[dso]="collection$ | async"
|
||||||
[comcolRole]="comcolRole"
|
[comcolRole]="comcolRole"
|
||||||
>
|
>
|
||||||
</ds-comcol-role>
|
</ds-comcol-role>
|
||||||
|
</div>
|
||||||
|
@@ -2,19 +2,19 @@
|
|||||||
<div class="container-fluid space-children-mr" *ngIf="shouldShow">
|
<div class="container-fluid space-children-mr" *ngIf="shouldShow">
|
||||||
<h3>{{ 'collection.source.controls.head' | translate }}</h3>
|
<h3>{{ 'collection.source.controls.head' | translate }}</h3>
|
||||||
<div>
|
<div>
|
||||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.status' | translate}}</span>
|
<span class="fw-bold">{{'collection.source.controls.harvest.status' | translate}}</span>
|
||||||
<span>{{contentSource?.harvestStatus}}</span>
|
<span>{{contentSource?.harvestStatus}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.start' | translate}}</span>
|
<span class="fw-bold">{{'collection.source.controls.harvest.start' | translate}}</span>
|
||||||
<span>{{contentSource?.harvestStartTime ? contentSource?.harvestStartTime : 'collection.source.controls.harvest.no-information'|translate }}</span>
|
<span>{{contentSource?.harvestStartTime ? contentSource?.harvestStartTime : 'collection.source.controls.harvest.no-information'|translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.last' | translate}}</span>
|
<span class="fw-bold">{{'collection.source.controls.harvest.last' | translate}}</span>
|
||||||
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
|
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.message' | translate}}</span>
|
<span class="fw-bold">{{'collection.source.controls.harvest.message' | translate}}</span>
|
||||||
<span>{{contentSource?.message ? contentSource?.message: 'collection.source.controls.harvest.no-information'|translate }}</span>
|
<span>{{contentSource?.message ? contentSource?.message: 'collection.source.controls.harvest.no-information'|translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="d-inline-block float-right space-children-mr">
|
<div class="d-inline-block float-end space-children-mr">
|
||||||
<button class=" btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
|
<button class=" btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
|
||||||
[dsBtnDisabled]="(hasChanges$ | async) !== true"
|
[dsBtnDisabled]="(hasChanges$ | async) !== true"
|
||||||
(click)="discard()"><i
|
(click)="discard()"><i
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
|
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-inline-block float-right ml-1 space-children-mr">
|
<div class="d-inline-block float-end ms-1 space-children-mr">
|
||||||
<button class=" btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
|
<button class=" btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
|
||||||
[dsBtnDisabled]="(hasChanges$ | async) !== true"
|
[dsBtnDisabled]="(hasChanges$ | async) !== true"
|
||||||
(click)="discard()"><i
|
(click)="discard()"><i
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
:host ::ng-deep #oaiSetContainer {
|
||||||
|
> :first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
@@ -23,6 +23,7 @@ import { getCollectionPageRoute } from '../collection-page-routing-paths';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-collection',
|
selector: 'ds-edit-collection',
|
||||||
templateUrl: '../../shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.html',
|
templateUrl: '../../shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.html',
|
||||||
|
styleUrls: ['./edit-collection-page.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
RouterLink,
|
RouterLink,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
|
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
|
||||||
class="example-tree-node expandable-node">
|
class="example-tree-node expandable-node">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button *ngIf="hasChild(null, node) | async" type="button" class="btn btn-default" cdkTreeNodeToggle
|
<button *ngIf="hasChild(null, node) | async" type="button" class="btn btn-default btn-transparent" cdkTreeNodeToggle
|
||||||
[attr.aria-label]="(node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) }"
|
[attr.aria-label]="(node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) }"
|
||||||
(click)="toggleExpanded(node)"
|
(click)="toggleExpanded(node)"
|
||||||
data-test="expand-button">
|
data-test="expand-button">
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<span class="d-flex align-middle my-auto">
|
<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="pe-2"> </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 *ngIf="node.payload.archivedItemsCount >= 0" class="badge rounded-pill bg-secondary align-top archived-items-lead my-auto ps-2 pe-2">{{node.payload.archivedItemsCount}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<div *ngIf="communityRD?.payload; let communityPayload">
|
<div *ngIf="communityRD?.payload; let communityPayload">
|
||||||
<ds-view-tracker [object]="communityPayload"></ds-view-tracker>
|
<ds-view-tracker [object]="communityPayload"></ds-view-tracker>
|
||||||
<div class="d-flex flex-row border-bottom mb-4 pb-4">
|
<div class="d-flex flex-row border-bottom mb-4 pb-4">
|
||||||
<header class="comcol-header mr-auto">
|
<header class="comcol-header me-auto">
|
||||||
<!-- Community name -->
|
<!-- Community name -->
|
||||||
<ds-comcol-page-header [name]="dsoNameService.getName(communityPayload)"></ds-comcol-page-header>
|
<ds-comcol-page-header [name]="dsoNameService.getName(communityPayload)"></ds-comcol-page-header>
|
||||||
<!-- Community logo -->
|
<!-- Community logo -->
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
<div class="col-12 pb-4">
|
<div class="col-12 pb-4">
|
||||||
<h1 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h1>
|
<h1 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h1>
|
||||||
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
||||||
<div class="form-group row">
|
<div class="mb-3 row">
|
||||||
<div class="col text-right space-children-mr">
|
<div class="col text-end space-children-mr">
|
||||||
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)">
|
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [dsBtnDisabled]="(processing$ | async)">
|
||||||
<i class="fas fa-times" aria-hidden="true"></i> {{'community.delete.cancel' | translate}}
|
<i class="fas fa-times" aria-hidden="true"></i> {{'community.delete.cancel' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
|
<div class="custom-alignment">
|
||||||
<ds-comcol-role
|
<ds-comcol-role
|
||||||
*ngFor="let comcolRole of comcolRoles$ | async"
|
*ngFor="let comcolRole of comcolRoles$ | async"
|
||||||
[dso]="community$ | async"
|
[dso]="community$ | async"
|
||||||
[comcolRole]="comcolRole"
|
[comcolRole]="comcolRole"
|
||||||
>
|
>
|
||||||
</ds-comcol-role>
|
</ds-comcol-role>
|
||||||
|
</div>
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
HTTP_INTERCEPTORS,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@@ -28,7 +32,7 @@ describe(`AuthInterceptor`, () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientTestingModule],
|
imports: [],
|
||||||
providers: [
|
providers: [
|
||||||
DspaceRestService,
|
DspaceRestService,
|
||||||
{ provide: AuthService, useValue: authServiceStub },
|
{ provide: AuthService, useValue: authServiceStub },
|
||||||
@@ -39,6 +43,8 @@ describe(`AuthInterceptor`, () => {
|
|||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
{ provide: Store, useValue: store },
|
{ provide: Store, useValue: store },
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -5,5 +5,6 @@ export enum AuthMethodType {
|
|||||||
Ip = 'ip',
|
Ip = 'ip',
|
||||||
X509 = 'x509',
|
X509 = 'x509',
|
||||||
Oidc = 'oidc',
|
Oidc = 'oidc',
|
||||||
Orcid = 'orcid'
|
Orcid = 'orcid',
|
||||||
|
Saml = 'saml'
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,11 @@ export class AuthMethod {
|
|||||||
this.location = location;
|
this.location = location;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'saml': {
|
||||||
|
this.authMethodType = AuthMethodType.Saml;
|
||||||
|
this.location = location;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// eslint-disable-next-line import/no-namespace
|
// eslint-disable-next-line import/no-namespace
|
||||||
import * as deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
|
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// eslint-disable-next-line import/no-namespace
|
// eslint-disable-next-line import/no-namespace
|
||||||
import * as deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
|
|
||||||
import { RestRequestMethod } from '../data/rest-request-method';
|
import { RestRequestMethod } from '../data/rest-request-method';
|
||||||
import { RemoveFromObjectCacheAction } from './object-cache.actions';
|
import { RemoveFromObjectCacheAction } from './object-cache.actions';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// eslint-disable-next-line import/no-namespace
|
// eslint-disable-next-line import/no-namespace
|
||||||
import * as deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
|
|
||||||
import { Relationship } from '../../shared/item-relationships/relationship.model';
|
import { Relationship } from '../../shared/item-relationships/relationship.model';
|
||||||
import { FieldChangeType } from './field-change-type.model';
|
import { FieldChangeType } from './field-change-type.model';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// eslint-disable-next-line import/no-namespace
|
// eslint-disable-next-line import/no-namespace
|
||||||
import * as deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RequestConfigureAction,
|
RequestConfigureAction,
|
||||||
|
194
src/app/core/dspace-rest/dspace-rest.interceptor.spec.ts
Normal file
194
src/app/core/dspace-rest/dspace-rest.interceptor.spec.ts
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import {
|
||||||
|
HTTP_INTERCEPTORS,
|
||||||
|
HttpClient,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
|
HttpClientTestingModule,
|
||||||
|
HttpTestingController,
|
||||||
|
} from '@angular/common/http/testing';
|
||||||
|
import { PLATFORM_ID } from '@angular/core';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {
|
||||||
|
APP_CONFIG,
|
||||||
|
AppConfig,
|
||||||
|
} from '../../../config/app-config.interface';
|
||||||
|
import { DspaceRestInterceptor } from './dspace-rest.interceptor';
|
||||||
|
import { DspaceRestService } from './dspace-rest.service';
|
||||||
|
|
||||||
|
describe('DspaceRestInterceptor', () => {
|
||||||
|
let httpMock: HttpTestingController;
|
||||||
|
let httpClient: HttpClient;
|
||||||
|
const appConfig: Partial<AppConfig> = {
|
||||||
|
rest: {
|
||||||
|
ssl: false,
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8080,
|
||||||
|
nameSpace: '/server',
|
||||||
|
baseUrl: 'http://api.example.com/server',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const appConfigWithSSR: Partial<AppConfig> = {
|
||||||
|
rest: {
|
||||||
|
ssl: false,
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8080,
|
||||||
|
nameSpace: '/server',
|
||||||
|
baseUrl: 'http://api.example.com/server',
|
||||||
|
ssrBaseUrl: 'http://ssr.example.com/server',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('When SSR base URL is not set ', () => {
|
||||||
|
describe('and it\'s in the browser', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
DspaceRestService,
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: DspaceRestInterceptor,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{ provide: APP_CONFIG, useValue: appConfig },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
|
httpClient = TestBed.inject(HttpClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify the request', () => {
|
||||||
|
const url = 'http://api.example.com/server/items';
|
||||||
|
httpClient.get(url).subscribe((response) => {
|
||||||
|
expect(response).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = httpMock.expectOne(url);
|
||||||
|
expect(req.request.url).toBe(url);
|
||||||
|
req.flush({});
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and it\'s in SSR mode', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
DspaceRestService,
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: DspaceRestInterceptor,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{ provide: APP_CONFIG, useValue: appConfig },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'server' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
|
httpClient = TestBed.inject(HttpClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not replace the base URL', () => {
|
||||||
|
const url = 'http://api.example.com/server/items';
|
||||||
|
|
||||||
|
httpClient.get(url).subscribe((response) => {
|
||||||
|
expect(response).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = httpMock.expectOne(url);
|
||||||
|
expect(req.request.url).toBe(url);
|
||||||
|
req.flush({});
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When SSR base URL is set ', () => {
|
||||||
|
describe('and it\'s in the browser', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
DspaceRestService,
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: DspaceRestInterceptor,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{ provide: APP_CONFIG, useValue: appConfigWithSSR },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
|
httpClient = TestBed.inject(HttpClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify the request', () => {
|
||||||
|
const url = 'http://api.example.com/server/items';
|
||||||
|
httpClient.get(url).subscribe((response) => {
|
||||||
|
expect(response).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = httpMock.expectOne(url);
|
||||||
|
expect(req.request.url).toBe(url);
|
||||||
|
req.flush({});
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and it\'s in SSR mode', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
DspaceRestService,
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: DspaceRestInterceptor,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{ provide: APP_CONFIG, useValue: appConfigWithSSR },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'server' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
|
httpClient = TestBed.inject(HttpClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace the base URL', () => {
|
||||||
|
const url = 'http://api.example.com/server/items';
|
||||||
|
const ssrBaseUrl = appConfigWithSSR.rest.ssrBaseUrl;
|
||||||
|
|
||||||
|
httpClient.get(url).subscribe((response) => {
|
||||||
|
expect(response).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = httpMock.expectOne(ssrBaseUrl + '/items');
|
||||||
|
expect(req.request.url).toBe(ssrBaseUrl + '/items');
|
||||||
|
req.flush({});
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not replace any query param containing the base URL', () => {
|
||||||
|
const url = 'http://api.example.com/server/items?url=http://api.example.com/server/item/1';
|
||||||
|
const ssrBaseUrl = appConfigWithSSR.rest.ssrBaseUrl;
|
||||||
|
|
||||||
|
httpClient.get(url).subscribe((response) => {
|
||||||
|
expect(response).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = httpMock.expectOne(ssrBaseUrl + '/items?url=http://api.example.com/server/item/1');
|
||||||
|
expect(req.request.url).toBe(ssrBaseUrl + '/items?url=http://api.example.com/server/item/1');
|
||||||
|
req.flush({});
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
52
src/app/core/dspace-rest/dspace-rest.interceptor.ts
Normal file
52
src/app/core/dspace-rest/dspace-rest.interceptor.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
|
import {
|
||||||
|
HttpEvent,
|
||||||
|
HttpHandler,
|
||||||
|
HttpInterceptor,
|
||||||
|
HttpRequest,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
PLATFORM_ID,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
APP_CONFIG,
|
||||||
|
AppConfig,
|
||||||
|
} from '../../../config/app-config.interface';
|
||||||
|
import { isEmpty } from '../../shared/empty.util';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
/**
|
||||||
|
* This Interceptor is used to use the configured base URL for the request made during SSR execution
|
||||||
|
*/
|
||||||
|
export class DspaceRestInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the configured application base URL
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected baseUrl: string;
|
||||||
|
protected ssrBaseUrl: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
|
@Inject(PLATFORM_ID) private platformId: string,
|
||||||
|
) {
|
||||||
|
this.baseUrl = this.appConfig.rest.baseUrl;
|
||||||
|
this.ssrBaseUrl = this.appConfig.rest.ssrBaseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
|
if (isPlatformBrowser(this.platformId) || isEmpty(this.ssrBaseUrl) || this.baseUrl === this.ssrBaseUrl) {
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Different SSR Base URL specified so replace it in the current request url
|
||||||
|
const url = request.url.replace(this.baseUrl, this.ssrBaseUrl);
|
||||||
|
const newRequest: HttpRequest<any> = request.clone({ url });
|
||||||
|
return next.handle(newRequest);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
HttpErrorResponse,
|
HttpErrorResponse,
|
||||||
HttpHeaders,
|
HttpHeaders,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
} from '@angular/common/http';
|
} from '@angular/common/http';
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import {
|
import {
|
||||||
inject,
|
inject,
|
||||||
@@ -33,8 +35,12 @@ describe('DspaceRestService', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientTestingModule],
|
imports: [],
|
||||||
providers: [DspaceRestService],
|
providers: [
|
||||||
|
DspaceRestService,
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
dspaceRestService = TestBed.inject(DspaceRestService);
|
dspaceRestService = TestBed.inject(DspaceRestService);
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
HTTP_INTERCEPTORS,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
@@ -21,7 +25,7 @@ describe('ForwardClientIpInterceptor', () => {
|
|||||||
clientIp = '1.2.3.4';
|
clientIp = '1.2.3.4';
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientTestingModule],
|
imports: [],
|
||||||
providers: [
|
providers: [
|
||||||
DspaceRestService,
|
DspaceRestService,
|
||||||
{
|
{
|
||||||
@@ -30,6 +34,8 @@ describe('ForwardClientIpInterceptor', () => {
|
|||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
{ provide: REQUEST, useValue: { get: () => undefined, connection: { remoteAddress: clientIp } } },
|
{ provide: REQUEST, useValue: { get: () => undefined, connection: { remoteAddress: clientIp } } },
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// eslint-disable-next-line import/no-namespace
|
// eslint-disable-next-line import/no-namespace
|
||||||
import * as deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AddToIndexAction,
|
AddToIndexAction,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// eslint-disable-next-line import/no-namespace
|
// eslint-disable-next-line import/no-namespace
|
||||||
import * as deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CommitPatchOperationsAction,
|
CommitPatchOperationsAction,
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
HTTP_INTERCEPTORS,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
@@ -25,7 +29,7 @@ describe(`LocaleInterceptor`, () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientTestingModule],
|
imports: [],
|
||||||
providers: [
|
providers: [
|
||||||
DspaceRestService,
|
DspaceRestService,
|
||||||
{
|
{
|
||||||
@@ -34,6 +38,8 @@ describe(`LocaleInterceptor`, () => {
|
|||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
{ provide: LocaleService, useValue: mockLocaleService },
|
{ provide: LocaleService, useValue: mockLocaleService },
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
HTTP_INTERCEPTORS,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@@ -39,10 +43,7 @@ describe('LogInterceptor', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [StoreModule.forRoot(appReducers, storeModuleConfig)],
|
||||||
HttpClientTestingModule,
|
|
||||||
StoreModule.forRoot(appReducers, storeModuleConfig),
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
DspaceRestService,
|
DspaceRestService,
|
||||||
// LogInterceptor,
|
// LogInterceptor,
|
||||||
@@ -55,6 +56,8 @@ describe('LogInterceptor', () => {
|
|||||||
{ provide: Router, useValue: router },
|
{ provide: Router, useValue: router },
|
||||||
{ provide: CorrelationIdService, useClass: CorrelationIdService },
|
{ provide: CorrelationIdService, useClass: CorrelationIdService },
|
||||||
{ provide: UUIDService, useClass: UUIDService },
|
{ provide: UUIDService, useClass: UUIDService },
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -186,6 +186,7 @@ export class HeadTagService {
|
|||||||
this.setCitationKeywordsTag();
|
this.setCitationKeywordsTag();
|
||||||
|
|
||||||
this.setCitationAbstractUrlTag();
|
this.setCitationAbstractUrlTag();
|
||||||
|
this.setCitationDoiTag();
|
||||||
this.setCitationPdfUrlTag();
|
this.setCitationPdfUrlTag();
|
||||||
this.setCitationPublisherTag();
|
this.setCitationPublisherTag();
|
||||||
|
|
||||||
@@ -198,7 +199,6 @@ export class HeadTagService {
|
|||||||
// this.setCitationIssueTag();
|
// this.setCitationIssueTag();
|
||||||
// this.setCitationFirstPageTag();
|
// this.setCitationFirstPageTag();
|
||||||
// this.setCitationLastPageTag();
|
// this.setCitationLastPageTag();
|
||||||
// this.setCitationDOITag();
|
|
||||||
// this.setCitationPMIDTag();
|
// this.setCitationPMIDTag();
|
||||||
|
|
||||||
// this.setCitationFullTextTag();
|
// this.setCitationFullTextTag();
|
||||||
@@ -319,6 +319,18 @@ export class HeadTagService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add <meta name="citation_doi" ... > to the <head>
|
||||||
|
*/
|
||||||
|
protected setCitationDoiTag(): void {
|
||||||
|
if (this.currentObject.value instanceof Item) {
|
||||||
|
const doi = this.getMetaTagValue('dc.identifier.doi');
|
||||||
|
if (hasValue(doi)) {
|
||||||
|
this.addMetaTag('citation_doi', doi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add <meta name="citation_pdf_url" ... > to the <head>
|
* Add <meta name="citation_pdf_url" ... > to the <head>
|
||||||
*/
|
*/
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { environment } from '../../../environments/environment.test';
|
||||||
import { ServerHardRedirectService } from './server-hard-redirect.service';
|
import { ServerHardRedirectService } from './server-hard-redirect.service';
|
||||||
|
|
||||||
describe('ServerHardRedirectService', () => {
|
describe('ServerHardRedirectService', () => {
|
||||||
@@ -7,7 +8,7 @@ describe('ServerHardRedirectService', () => {
|
|||||||
const mockRequest = jasmine.createSpyObj(['get']);
|
const mockRequest = jasmine.createSpyObj(['get']);
|
||||||
const mockResponse = jasmine.createSpyObj(['redirect', 'end']);
|
const mockResponse = jasmine.createSpyObj(['redirect', 'end']);
|
||||||
|
|
||||||
const service: ServerHardRedirectService = new ServerHardRedirectService(mockRequest, mockResponse);
|
let service: ServerHardRedirectService = new ServerHardRedirectService(environment, mockRequest, mockResponse);
|
||||||
const origin = 'https://test-host.com:4000';
|
const origin = 'https://test-host.com:4000';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -68,4 +69,23 @@ describe('ServerHardRedirectService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when SSR base url is set', () => {
|
||||||
|
const redirect = 'https://private-url:4000/server/api/bitstreams/uuid';
|
||||||
|
const replacedUrl = 'https://public-url/server/api/bitstreams/uuid';
|
||||||
|
const environmentWithSSRUrl: any = { ...environment, ...{ ...environment.rest, rest: {
|
||||||
|
ssrBaseUrl: 'https://private-url:4000/server',
|
||||||
|
baseUrl: 'https://public-url/server',
|
||||||
|
} } };
|
||||||
|
service = new ServerHardRedirectService(environmentWithSSRUrl, mockRequest, mockResponse);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service.redirect(redirect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should perform a 302 redirect', () => {
|
||||||
|
expect(mockResponse.redirect).toHaveBeenCalledWith(302, replacedUrl);
|
||||||
|
expect(mockResponse.end).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -7,10 +7,15 @@ import {
|
|||||||
Response,
|
Response,
|
||||||
} from 'express';
|
} from 'express';
|
||||||
|
|
||||||
|
import {
|
||||||
|
APP_CONFIG,
|
||||||
|
AppConfig,
|
||||||
|
} from '../../../config/app-config.interface';
|
||||||
import {
|
import {
|
||||||
REQUEST,
|
REQUEST,
|
||||||
RESPONSE,
|
RESPONSE,
|
||||||
} from '../../../express.tokens';
|
} from '../../../express.tokens';
|
||||||
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { HardRedirectService } from './hard-redirect.service';
|
import { HardRedirectService } from './hard-redirect.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,6 +25,7 @@ import { HardRedirectService } from './hard-redirect.service';
|
|||||||
export class ServerHardRedirectService extends HardRedirectService {
|
export class ServerHardRedirectService extends HardRedirectService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
@Inject(REQUEST) protected req: Request,
|
@Inject(REQUEST) protected req: Request,
|
||||||
@Inject(RESPONSE) protected res: Response,
|
@Inject(RESPONSE) protected res: Response,
|
||||||
) {
|
) {
|
||||||
@@ -35,17 +41,22 @@ export class ServerHardRedirectService extends HardRedirectService {
|
|||||||
* optional HTTP status code to use for redirect (default = 302, which is a temporary redirect)
|
* optional HTTP status code to use for redirect (default = 302, which is a temporary redirect)
|
||||||
*/
|
*/
|
||||||
redirect(url: string, statusCode?: number) {
|
redirect(url: string, statusCode?: number) {
|
||||||
|
|
||||||
if (url === this.req.url) {
|
if (url === this.req.url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let redirectUrl = url;
|
||||||
|
// If redirect url contains SSR base url then replace with public base url
|
||||||
|
if (isNotEmpty(this.appConfig.rest.ssrBaseUrl) && this.appConfig.rest.baseUrl !== this.appConfig.rest.ssrBaseUrl) {
|
||||||
|
redirectUrl = url.replace(this.appConfig.rest.ssrBaseUrl, this.appConfig.rest.baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.res.finished) {
|
if (this.res.finished) {
|
||||||
const req: any = this.req;
|
const req: any = this.req;
|
||||||
req._r_count = (req._r_count || 0) + 1;
|
req._r_count = (req._r_count || 0) + 1;
|
||||||
|
|
||||||
console.warn('Attempted to redirect on a finished response. From',
|
console.warn('Attempted to redirect on a finished response. From',
|
||||||
this.req.url, 'to', url);
|
this.req.url, 'to', redirectUrl);
|
||||||
|
|
||||||
if (req._r_count > 10) {
|
if (req._r_count > 10) {
|
||||||
console.error('Detected a redirection loop. killing the nodejs process');
|
console.error('Detected a redirection loop. killing the nodejs process');
|
||||||
@@ -59,9 +70,9 @@ export class ServerHardRedirectService extends HardRedirectService {
|
|||||||
status = 302;
|
status = 302;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Redirecting from ${this.req.url} to ${url} with ${status}`);
|
console.info(`Redirecting from ${this.req.url} to ${redirectUrl} with ${status}`);
|
||||||
|
|
||||||
this.res.redirect(status, url);
|
this.res.redirect(status, redirectUrl);
|
||||||
this.res.end();
|
this.res.end();
|
||||||
// I haven't found a way to correctly stop Angular rendering.
|
// I haven't found a way to correctly stop Angular rendering.
|
||||||
// So we just let it end its work, though we have already closed
|
// So we just let it end its work, though we have already closed
|
||||||
|
@@ -29,6 +29,9 @@ export enum Context {
|
|||||||
SideBarSearchModal = 'sideBarSearchModal',
|
SideBarSearchModal = 'sideBarSearchModal',
|
||||||
SideBarSearchModalCurrent = 'sideBarSearchModalCurrent',
|
SideBarSearchModalCurrent = 'sideBarSearchModalCurrent',
|
||||||
|
|
||||||
|
ScopeSelectorModal = 'scopeSelectorModal',
|
||||||
|
ScopeSelectorModalCurrent = 'scopeSelectorModalCurrent',
|
||||||
|
|
||||||
/** The MyDSpace* Context values below are used for badge display in MyDSpace. */
|
/** The MyDSpace* Context values below are used for badge display in MyDSpace. */
|
||||||
MyDSpaceArchived = 'mydspaceArchived',
|
MyDSpaceArchived = 'mydspaceArchived',
|
||||||
MyDSpaceWorkspace = 'mydspaceWorkspace',
|
MyDSpaceWorkspace = 'mydspaceWorkspace',
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
HttpClient,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import {
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
@@ -17,8 +21,12 @@ describe(`BrowserXSRFService`, () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [ HttpClientTestingModule ],
|
imports: [],
|
||||||
providers: [ BrowserXSRFService ],
|
providers: [
|
||||||
|
BrowserXSRFService,
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
httpClient = TestBed.inject(HttpClient);
|
httpClient = TestBed.inject(HttpClient);
|
||||||
httpTestingController = TestBed.inject(HttpTestingController);
|
httpTestingController = TestBed.inject(HttpTestingController);
|
||||||
|
@@ -2,10 +2,12 @@ import {
|
|||||||
HTTP_INTERCEPTORS,
|
HTTP_INTERCEPTORS,
|
||||||
HttpHeaders,
|
HttpHeaders,
|
||||||
HttpXsrfTokenExtractor,
|
HttpXsrfTokenExtractor,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
} from '@angular/common/http';
|
} from '@angular/common/http';
|
||||||
import {
|
import {
|
||||||
HttpClientTestingModule,
|
|
||||||
HttpTestingController,
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
} from '@angular/common/http/testing';
|
} from '@angular/common/http/testing';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
@@ -20,7 +22,7 @@ import { XsrfInterceptor } from './xsrf.interceptor';
|
|||||||
describe(`XsrfInterceptor`, () => {
|
describe(`XsrfInterceptor`, () => {
|
||||||
let service: DspaceRestService;
|
let service: DspaceRestService;
|
||||||
let httpMock: HttpTestingController;
|
let httpMock: HttpTestingController;
|
||||||
let cookieService: CookieService;
|
let cookieService: CookieServiceMock;
|
||||||
|
|
||||||
// mock XSRF token
|
// mock XSRF token
|
||||||
const testToken = 'test-token';
|
const testToken = 'test-token';
|
||||||
@@ -33,45 +35,52 @@ describe(`XsrfInterceptor`, () => {
|
|||||||
const mockStatusCode = 200;
|
const mockStatusCode = 200;
|
||||||
const mockStatusText = 'SUCCESS';
|
const mockStatusText = 'SUCCESS';
|
||||||
|
|
||||||
|
const testUrl = 'https://rest.com/server/api/core/items';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
const tokenExtractor = new HttpXsrfTokenExtractorMock(testToken);
|
||||||
|
cookieService = new CookieServiceMock();
|
||||||
|
const interceptor = new XsrfInterceptor(tokenExtractor,cookieService as any);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientTestingModule],
|
imports: [],
|
||||||
providers: [
|
providers: [
|
||||||
DspaceRestService,
|
DspaceRestService,
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
useClass: XsrfInterceptor,
|
useValue: interceptor,
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
{ provide: HttpXsrfTokenExtractor, useValue: new HttpXsrfTokenExtractorMock(testToken) },
|
{ provide: HttpXsrfTokenExtractor, useValue: tokenExtractor },
|
||||||
{ provide: CookieService, useValue: new CookieServiceMock() },
|
{ provide: CookieService, useValue: cookieService },
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
service = TestBed.get(DspaceRestService);
|
service = TestBed.get(DspaceRestService);
|
||||||
httpMock = TestBed.get(HttpTestingController);
|
httpMock = TestBed.get(HttpTestingController);
|
||||||
cookieService = TestBed.get(CookieService);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change withCredentials to true at all times', (done) => {
|
it('should change withCredentials to true at all times', (done) => {
|
||||||
service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => {
|
service.request(RestRequestMethod.POST, testUrl, 'test', { withCredentials: false }).subscribe((response) => {
|
||||||
expect(response).toBeTruthy();
|
expect(response).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpRequest = httpMock.expectOne('server/api/core/items');
|
const httpRequest = httpMock.expectOne(testUrl);
|
||||||
expect(httpRequest.request.withCredentials).toBeTrue();
|
expect(httpRequest.request.withCredentials).toBeTrue();
|
||||||
|
|
||||||
httpRequest.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText });
|
httpRequest.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add an X-XSRF-TOKEN header when we are sending an HTTP POST request', (done) => {
|
it('should add an X-XSRF-TOKEN header when we are sending an HTTP POST request', (done) => {
|
||||||
service.request(RestRequestMethod.POST, 'server/api/core/items', 'test').subscribe((response) => {
|
service.request(RestRequestMethod.POST, testUrl, 'test').subscribe((response) => {
|
||||||
expect(response).toBeTruthy();
|
expect(response).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpRequest = httpMock.expectOne('server/api/core/items');
|
const httpRequest = httpMock.expectOne(testUrl);
|
||||||
|
|
||||||
expect(httpRequest.request.headers.has('X-XSRF-TOKEN')).toBeTrue();
|
expect(httpRequest.request.headers.has('X-XSRF-TOKEN')).toBeTrue();
|
||||||
expect(httpRequest.request.withCredentials).toBeTrue();
|
expect(httpRequest.request.withCredentials).toBeTrue();
|
||||||
@@ -83,12 +92,12 @@ describe(`XsrfInterceptor`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT add an X-XSRF-TOKEN header when we are sending an HTTP GET request', (done) => {
|
it('should NOT add an X-XSRF-TOKEN header when we are sending an HTTP GET request', (done) => {
|
||||||
service.request(RestRequestMethod.GET, 'server/api/core/items').subscribe((response) => {
|
service.request(RestRequestMethod.GET, testUrl).subscribe((response) => {
|
||||||
expect(response).toBeTruthy();
|
expect(response).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpRequest = httpMock.expectOne('server/api/core/items');
|
const httpRequest = httpMock.expectOne(testUrl);
|
||||||
|
|
||||||
expect(httpRequest.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
|
expect(httpRequest.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
|
||||||
expect(httpRequest.request.withCredentials).toBeTrue();
|
expect(httpRequest.request.withCredentials).toBeTrue();
|
||||||
@@ -115,7 +124,7 @@ describe(`XsrfInterceptor`, () => {
|
|||||||
// Create a mock XSRF token to be returned in response within DSPACE-XSRF-TOKEN header
|
// Create a mock XSRF token to be returned in response within DSPACE-XSRF-TOKEN header
|
||||||
const mockNewXSRFToken = '123456789abcdefg';
|
const mockNewXSRFToken = '123456789abcdefg';
|
||||||
|
|
||||||
service.request(RestRequestMethod.GET, 'server/api/core/items').subscribe((response) => {
|
service.request(RestRequestMethod.GET, testUrl).subscribe((response) => {
|
||||||
expect(response).toBeTruthy();
|
expect(response).toBeTruthy();
|
||||||
|
|
||||||
// ensure mock data (added in below flush() call) is returned.
|
// ensure mock data (added in below flush() call) is returned.
|
||||||
@@ -135,7 +144,7 @@ describe(`XsrfInterceptor`, () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpRequest = httpMock.expectOne('server/api/core/items');
|
const httpRequest = httpMock.expectOne(testUrl);
|
||||||
|
|
||||||
// Flush & create mock response (including sending back a new XSRF token in header)
|
// Flush & create mock response (including sending back a new XSRF token in header)
|
||||||
httpRequest.flush(mockPayload, {
|
httpRequest.flush(mockPayload, {
|
||||||
@@ -153,7 +162,7 @@ describe(`XsrfInterceptor`, () => {
|
|||||||
const mockErrorText = 'Forbidden';
|
const mockErrorText = 'Forbidden';
|
||||||
const mockErrorMessage = 'CSRF token mismatch';
|
const mockErrorMessage = 'CSRF token mismatch';
|
||||||
|
|
||||||
service.request(RestRequestMethod.GET, 'server/api/core/items').subscribe({
|
service.request(RestRequestMethod.GET, testUrl).subscribe({
|
||||||
error: (error: unknown) => {
|
error: (error: unknown) => {
|
||||||
expect(error).toBeTruthy();
|
expect(error).toBeTruthy();
|
||||||
expect(error instanceof RequestError).toBeTrue();
|
expect(error instanceof RequestError).toBeTrue();
|
||||||
@@ -170,7 +179,7 @@ describe(`XsrfInterceptor`, () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpRequest = httpMock.expectOne('server/api/core/items');
|
const httpRequest = httpMock.expectOne(testUrl);
|
||||||
|
|
||||||
// Flush & create mock error response (including sending back a new XSRF token in header)
|
// Flush & create mock error response (including sending back a new XSRF token in header)
|
||||||
httpRequest.flush(mockErrorMessage, {
|
httpRequest.flush(mockErrorMessage, {
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
<form [formGroup]="form" (ngSubmit)="submit()">
|
<form [formGroup]="form" (ngSubmit)="submit()">
|
||||||
<div class="form-group">
|
<div class="mb-3">
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<div class="col-12 col-sm-6">
|
<div class="col-12 col-sm-6">
|
||||||
<label class="font-weight-bold" for="task">{{'curation.form.task-select.label' |translate }}</label>
|
<label class="fw-bold form-label" for="task">{{'curation.form.task-select.label' |translate }}</label>
|
||||||
<select id="task" formControlName="task" class="form-control">
|
<select id="task" formControlName="task" class="form-select">
|
||||||
<option *ngFor="let task of tasks" [value]="task">
|
<option *ngFor="let task of tasks" [value]="task">
|
||||||
{{ 'curation-task.task.' + task + '.label' | translate }}
|
{{ 'curation-task.task.' + task + '.label' | translate }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!hasHandleValue()" class="col-12 col-sm-6">
|
<div *ngIf="!hasHandleValue()" class="col-12 col-sm-6">
|
||||||
<label class="font-weight-bold" for="handle">{{'curation.form.handle.label' |translate }}</label>
|
<label class="fw-bold form-label" for="handle">{{'curation.form.handle.label' |translate }}</label>
|
||||||
<input id="handle" class="form-control" formControlName="handle">
|
<input id="handle" class="form-control" formControlName="handle">
|
||||||
<small class="text-muted">{{'curation.form.handle.hint' |translate }}</small>
|
<small class="text-muted">{{'curation.form.handle.hint' |translate }}</small>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -24,26 +24,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.ds-warning {
|
&.ds-warning {
|
||||||
background-color: var(--ds-warning-bg);
|
background-color: var(--ds-warning-bg-subtle);
|
||||||
|
|
||||||
.ds-flex-cell {
|
.ds-flex-cell {
|
||||||
border: 1px solid var(--bs-warning);
|
border: 1px solid var(--bs-warning-border-subtle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ds-danger {
|
&.ds-danger {
|
||||||
background-color: var(--ds-danger-bg);
|
background-color: var(--ds-danger-bg-subtle);
|
||||||
|
|
||||||
.ds-flex-cell {
|
.ds-flex-cell {
|
||||||
border: 1px solid var(--bs-danger);
|
border: 1px solid var(--bs-danger-border-subtle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ds-success {
|
&.ds-success {
|
||||||
background-color: var(--ds-success-bg);
|
background-color: var(--ds-success-bg-subtle);
|
||||||
|
|
||||||
.ds-flex-cell {
|
.ds-flex-cell {
|
||||||
border: 1px solid var(--bs-success);
|
border: 1px solid var(--bs-success-border-subtle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
{{ (enabledFreeTextEditing ? dsoType + '.edit.metadata.edit.buttons.disable-free-text-editing' : dsoType + '.edit.metadata.edit.buttons.enable-free-text-editing') | translate }}
|
{{ (enabledFreeTextEditing ? dsoType + '.edit.metadata.edit.buttons.disable-free-text-editing' : dsoType + '.edit.metadata.edit.buttons.enable-free-text-editing') | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div *ngIf="!isVirtual && !mdValue.editing && mdValue.newValue.authority && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_UNSET && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_NOVALUE">
|
<div *ngIf="!isVirtual && !mdValue.editing && mdValue.newValue.authority && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_UNSET && mdValue.newValue.confidence !== ConfidenceTypeEnum.CF_NOVALUE">
|
||||||
<span class="badge badge-light border" >
|
<span class="badge bg-light border" >
|
||||||
<i dsAuthorityConfidenceState
|
<i dsAuthorityConfidenceState
|
||||||
class="fas fa-fw p-0"
|
class="fas fa-fw p-0"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
<div class="mt-2" *ngIf=" mdValue.editing && (isAuthorityControlled() | async) && (isSuggesterVocabulary() | async)">
|
<div class="mt-2" *ngIf=" mdValue.editing && (isAuthorityControlled() | async) && (isSuggesterVocabulary() | async)">
|
||||||
<div class="btn-group w-75">
|
<div class="btn-group w-75">
|
||||||
<i dsAuthorityConfidenceState
|
<i dsAuthorityConfidenceState
|
||||||
class="fas fa-fw p-0 mr-1 mt-auto mb-auto"
|
class="fas fa-fw p-0 me-1 mt-auto mb-auto"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
[authorityValue]="mdValue.newValue.confidence"
|
[authorityValue]="mdValue.newValue.confidence"
|
||||||
[iconMode]="true"
|
[iconMode]="true"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex" *ngIf="mdRepresentation">
|
<div class="d-flex" *ngIf="mdRepresentation">
|
||||||
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
<a class="me-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
||||||
<ds-type-badge [object]="mdRepresentation"></ds-type-badge>
|
<ds-type-badge [object]="mdRepresentation"></ds-type-badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -14,3 +14,6 @@
|
|||||||
.cdk-drag-placeholder {
|
.cdk-drag-placeholder {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
.badge.bg-light {
|
||||||
|
color: var(--bs-gray-900);
|
||||||
|
}
|
||||||
|
@@ -1,24 +1,24 @@
|
|||||||
<div class="item-metadata" *ngIf="form">
|
<div class="item-metadata" *ngIf="form">
|
||||||
<div class="button-row top d-flex my-2 space-children-mr ml-gap">
|
<div class="button-row top d-flex my-2 space-children-mr ms-gap">
|
||||||
<button class="mr-auto btn btn-success" id="dso-add-btn" [dsBtnDisabled]="form.newValue || (saving$ | async)"
|
<button class="me-auto btn btn-success" id="dso-add-btn" [dsBtnDisabled]="form.newValue || (saving$ | async)"
|
||||||
[attr.aria-label]="dsoType + '.edit.metadata.add-button' | translate"
|
[attr.aria-label]="dsoType + '.edit.metadata.add-button' | translate"
|
||||||
[title]="dsoType + '.edit.metadata.add-button' | translate"
|
[title]="dsoType + '.edit.metadata.add-button' | translate"
|
||||||
(click)="add()"><i class="fas fa-plus" aria-hidden="true"></i>
|
(click)="add()"><i class="fas fa-plus" aria-hidden="true"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.add-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.add-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-warning ml-1" id="dso-reinstate-btn" *ngIf="isReinstatable" [dsBtnDisabled]="(saving$ | async)"
|
<button class="btn btn-warning ms-1" id="dso-reinstate-btn" *ngIf="isReinstatable" [dsBtnDisabled]="(saving$ | async)"
|
||||||
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
||||||
[title]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
[title]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
||||||
(click)="reinstate()"><i class="fas fa-undo-alt" aria-hidden="true"></i>
|
(click)="reinstate()"><i class="fas fa-undo-alt" aria-hidden="true"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary ml-1" id="dso-save-btn" [dsBtnDisabled]="!hasChanges || (saving$ | async)"
|
<button class="btn btn-primary ms-1" id="dso-save-btn" [dsBtnDisabled]="!hasChanges || (saving$ | async)"
|
||||||
[attr.aria-label]="dsoType + '.edit.metadata.save-button' | translate"
|
[attr.aria-label]="dsoType + '.edit.metadata.save-button' | translate"
|
||||||
[title]="dsoType + '.edit.metadata.save-button' | translate"
|
[title]="dsoType + '.edit.metadata.save-button' | translate"
|
||||||
(click)="submit()"><i class="fas fa-save" aria-hidden="true"></i>
|
(click)="submit()"><i class="fas fa-save" aria-hidden="true"></i>
|
||||||
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.save-button' | translate }}</span>
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.save-button' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger ml-1" id="dso-discard-btn" *ngIf="!isReinstatable"
|
<button class="btn btn-danger ms-1" id="dso-discard-btn" *ngIf="!isReinstatable"
|
||||||
[attr.aria-label]="dsoType + '.edit.metadata.discard-button' | translate"
|
[attr.aria-label]="dsoType + '.edit.metadata.discard-button' | translate"
|
||||||
[title]="dsoType + '.edit.metadata.discard-button' | translate"
|
[title]="dsoType + '.edit.metadata.discard-button' | translate"
|
||||||
[dsBtnDisabled]="!hasChanges || (saving$ | async)"
|
[dsBtnDisabled]="!hasChanges || (saving$ | async)"
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
<ds-alert [content]="dsoType + '.edit.metadata.empty'" [type]="AlertTypeEnum.Info"></ds-alert>
|
<ds-alert [content]="dsoType + '.edit.metadata.empty'" [type]="AlertTypeEnum.Info"></ds-alert>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-row bottom d-inline-block w-100">
|
<div class="button-row bottom d-inline-block w-100">
|
||||||
<div class="mt-2 float-right space-children-mr ml-gap">
|
<div class="mt-2 float-end space-children-mr ms-gap">
|
||||||
<button class="btn btn-warning" *ngIf="isReinstatable" [dsBtnDisabled]="(saving$ | async)"
|
<button class="btn btn-warning" *ngIf="isReinstatable" [dsBtnDisabled]="(saving$ | async)"
|
||||||
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
[attr.aria-label]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
||||||
[title]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
[title]="dsoType + '.edit.metadata.reinstate-button' | translate"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<div class="position-absolute ml-1">
|
<div class="position-absolute ms-1">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="linkType !== linkTypes.None"
|
<a *ngIf="linkType !== linkTypes.None"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<div class="position-absolute ml-1">
|
<div class="position-absolute ms-1">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="linkType !== linkTypes.None"
|
<a *ngIf="linkType !== linkTypes.None"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<div class="position-absolute ml-1">
|
<div class="position-absolute ms-1">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="linkType !== linkTypes.None"
|
<a *ngIf="linkType !== linkTypes.None"
|
||||||
|
@@ -17,6 +17,8 @@ import { TruncatablePartComponent } from '../../../../../shared/truncatable/trun
|
|||||||
|
|
||||||
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
||||||
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
||||||
|
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement, Context.ScopeSelectorModal)
|
||||||
|
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement, Context.ScopeSelectorModalCurrent)
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-journal-issue-sidebar-search-list-element',
|
selector: 'ds-journal-issue-sidebar-search-list-element',
|
||||||
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
||||||
|
@@ -17,6 +17,8 @@ import { TruncatablePartComponent } from '../../../../../shared/truncatable/trun
|
|||||||
|
|
||||||
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
||||||
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
||||||
|
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement, Context.ScopeSelectorModal)
|
||||||
|
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement, Context.ScopeSelectorModalCurrent)
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-journal-volume-sidebar-search-list-element',
|
selector: 'ds-journal-volume-sidebar-search-list-element',
|
||||||
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
||||||
|
@@ -17,6 +17,8 @@ import { TruncatablePartComponent } from '../../../../../shared/truncatable/trun
|
|||||||
|
|
||||||
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
||||||
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
||||||
|
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement, Context.ScopeSelectorModal)
|
||||||
|
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement, Context.ScopeSelectorModalCurrent)
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-journal-sidebar-search-list-element',
|
selector: 'ds-journal-sidebar-search-list-element',
|
||||||
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
<ds-item-page-title-field [item]="object" class="me-auto">
|
||||||
</ds-item-page-title-field>
|
</ds-item-page-title-field>
|
||||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
<ds-item-page-title-field [item]="object" class="me-auto">
|
||||||
</ds-item-page-title-field>
|
</ds-item-page-title-field>
|
||||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
<ds-item-page-title-field [item]="object" class="me-auto">
|
||||||
</ds-item-page-title-field>
|
</ds-item-page-title-field>
|
||||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 w-100">
|
<div class="mt-5 w-100 px-0">
|
||||||
<ds-tabbed-related-entities-search [item]="object"
|
<ds-tabbed-related-entities-search [item]="object"
|
||||||
[relationTypes]="[{
|
[relationTypes]="[{
|
||||||
label: 'isJournalOfPublication',
|
label: 'isJournalOfPublication',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<div class="position-absolute ml-1">
|
<div class="position-absolute ms-1">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="linkType !== linkTypes.None"
|
<a *ngIf="linkType !== linkTypes.None"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<div class="position-absolute ml-1">
|
<div class="position-absolute ms-1">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="linkType !== linkTypes.None"
|
<a *ngIf="linkType !== linkTypes.None"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<div class="position-absolute ml-1">
|
<div class="position-absolute ms-1">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<a *ngIf="linkType !== linkTypes.None"
|
<a *ngIf="linkType !== linkTypes.None"
|
||||||
|
@@ -16,6 +16,8 @@ import { TruncatablePartComponent } from '../../../../../shared/truncatable/trun
|
|||||||
|
|
||||||
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
||||||
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
||||||
|
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.ScopeSelectorModal)
|
||||||
|
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.ScopeSelectorModalCurrent)
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-org-unit-sidebar-search-list-element',
|
selector: 'ds-org-unit-sidebar-search-list-element',
|
||||||
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
||||||
|
@@ -23,6 +23,8 @@ import { TruncatablePartComponent } from '../../../../../shared/truncatable/trun
|
|||||||
|
|
||||||
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
||||||
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
||||||
|
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.ScopeSelectorModal)
|
||||||
|
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.ScopeSelectorModalCurrent)
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-person-sidebar-search-list-element',
|
selector: 'ds-person-sidebar-search-list-element',
|
||||||
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
||||||
|
@@ -16,6 +16,8 @@ import { TruncatablePartComponent } from '../../../../../shared/truncatable/trun
|
|||||||
|
|
||||||
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement, Context.SideBarSearchModal)
|
||||||
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent)
|
||||||
|
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement, Context.ScopeSelectorModal)
|
||||||
|
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement, Context.ScopeSelectorModalCurrent)
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-project-sidebar-search-list-element',
|
selector: 'ds-project-sidebar-search-list-element',
|
||||||
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
templateUrl: '../../../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
<ds-item-page-title-field [item]="object" class="me-auto">
|
||||||
</ds-item-page-title-field>
|
</ds-item-page-title-field>
|
||||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 w-100">
|
<div class="mt-5 w-100 px-0">
|
||||||
<ds-tabbed-related-entities-search [item]="object"
|
<ds-tabbed-related-entities-search [item]="object"
|
||||||
[relationTypes]="[{
|
[relationTypes]="[{
|
||||||
label: 'isOrgUnitOfPerson',
|
label: 'isOrgUnitOfPerson',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field class="mr-auto" [item]="object">
|
<ds-item-page-title-field class="me-auto" [item]="object">
|
||||||
</ds-item-page-title-field>
|
</ds-item-page-title-field>
|
||||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 w-100">
|
<div class="mt-5 w-100 px-0">
|
||||||
<ds-tabbed-related-entities-search [item]="object"
|
<ds-tabbed-related-entities-search [item]="object"
|
||||||
[relationTypes]="[{
|
[relationTypes]="[{
|
||||||
label: 'isAuthorOfPublication',
|
label: 'isAuthorOfPublication',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
<ds-results-back-button *ngIf="showBackButton$ | async" [back]="back"></ds-results-back-button>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
<ds-item-page-title-field [item]="object" class="me-auto">
|
||||||
</ds-item-page-title-field>
|
</ds-item-page-title-field>
|
||||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<!-- <div class="person-thumbnail pr-2">-->
|
<!-- <div class="person-thumbnail pe-2">-->
|
||||||
<!-- <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-themed-thumbnail>-->
|
<!-- <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-themed-thumbnail>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">New name variant</h4>
|
<h4 class="modal-title">New name variant</h4>
|
||||||
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
|
<button type="button" class="btn-close" aria-label="Close" (click)="modal.dismiss('Cross click')">
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
@@ -43,7 +43,7 @@ describe('NameVariantModalComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('when close button is clicked, dismiss should be called on the modal', () => {
|
it('when close button is clicked, dismiss should be called on the modal', () => {
|
||||||
debugElement.query(By.css('button.close')).triggerEventHandler('click', {});
|
debugElement.query(By.css('button.btn-close')).triggerEventHandler('click', {});
|
||||||
expect(modal.dismiss).toHaveBeenCalled();
|
expect(modal.dismiss).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<footer class="text-lg-start">
|
<footer>
|
||||||
<div *ngIf="showTopFooter" class="top-footer">
|
<div *ngIf="showTopFooter" class="top-footer">
|
||||||
<!-- Grid container -->
|
<!-- Grid container -->
|
||||||
<div class=" container p-4">
|
<div class=" container p-4">
|
||||||
|
@@ -68,6 +68,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<span class="font-weight-bold">{{'forgot-password.form.identification.email' | translate}} </span>
|
<span class="fw-bold form-label">{{'forgot-password.form.identification.email' | translate}} </span>
|
||||||
<span [attr.data-test]="'email' | dsBrowserOnly">{{(registration$ |async).email}}</span>
|
<span [attr.data-test]="'email' | dsBrowserOnly">{{(registration$ |async).email}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -56,11 +56,9 @@ describe('ContextHelpToggleComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('clicking the button should toggle context help icon visibility', fakeAsync(() => {
|
it('clicking the button should toggle context help icon visibility', fakeAsync(() => {
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
fixture.debugElement.query(By.css('a')).nativeElement.click();
|
fixture.debugElement.query(By.css('a')).nativeElement.click();
|
||||||
tick();
|
tick();
|
||||||
expect(contextHelpService.toggleIcons).toHaveBeenCalled();
|
expect(contextHelpService.toggleIcons).toHaveBeenCalled();
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user