From 868b0211186e9c1d4507d5afafc4ff42d7b5d92b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 02:35:56 +0000 Subject: [PATCH 001/395] Bump @babel/runtime from 7.26.0 to 7.26.7 Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.0 to 7.26.7. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-runtime) --- updated-dependencies: - dependency-name: "@babel/runtime" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e94785d93e5..cd5c2e64615 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@angular/platform-browser-dynamic": "^15.2.10", "@angular/platform-server": "^15.2.10", "@angular/router": "^15.2.10", - "@babel/runtime": "7.26.0", + "@babel/runtime": "7.26.7", "@kolkov/ngx-gallery": "^2.0.1", "@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-dynamic-forms/core": "^15.0.0", diff --git a/yarn.lock b/yarn.lock index 58d8f59fbcc..23db53d7cf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1352,10 +1352,10 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@7.26.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== +"@babel/runtime@7.26.7", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" + integrity sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ== dependencies: regenerator-runtime "^0.14.0" From 59b39f12d6a9439187726249aa2eb45fabe1894a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 03:03:49 +0000 Subject: [PATCH 002/395] Bump @types/lodash from 4.17.14 to 4.17.15 Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.14 to 4.17.15. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a8849d0859..da4f537ea85 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,7 @@ "@types/grecaptcha": "^3.0.9", "@types/jasmine": "~3.6.0", "@types/js-cookie": "2.2.6", - "@types/lodash": "^4.17.14", + "@types/lodash": "^4.17.15", "@types/node": "^14.18.63", "@types/sanitize-html": "^2.13.0", "@typescript-eslint/eslint-plugin": "^5.62.0", diff --git a/yarn.lock b/yarn.lock index 86a07c555ad..dab1d7a5e57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2539,10 +2539,10 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash@^4.17.14": - version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.14.tgz#bafc053533f4cdc5fcc9635af46a963c1f3deaff" - integrity sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A== +"@types/lodash@^4.17.15": + version "4.17.15" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.15.tgz#12d4af0ed17cc7600ce1f9980cec48fc17ad1e89" + integrity sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw== "@types/mime@*": version "3.0.1" From 45cc177cab8b5d629d9aec86451fda42fb7a2a0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 03:04:40 +0000 Subject: [PATCH 003/395] Bump isbot from 5.1.21 to 5.1.22 Bumps [isbot](https://github.com/omrilotan/isbot) from 5.1.21 to 5.1.22. - [Changelog](https://github.com/omrilotan/isbot/blob/main/CHANGELOG.md) - [Commits](https://github.com/omrilotan/isbot/compare/v5.1.21...v5.1.22) --- updated-dependencies: - dependency-name: isbot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a8849d0859..2ff8ab1e28c 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "filesize": "^6.1.0", "http-proxy-middleware": "^2.0.7", "http-terminator": "^3.2.0", - "isbot": "^5.1.21", + "isbot": "^5.1.22", "js-cookie": "2.2.1", "js-yaml": "^4.1.0", "json5": "^2.2.3", diff --git a/yarn.lock b/yarn.lock index 86a07c555ad..364e6366ec0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7080,10 +7080,10 @@ isbinaryfile@^4.0.8: resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz" integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== -isbot@^5.1.21: - version "5.1.21" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.21.tgz#4e27526a71c8b9c243b1c7a6445ad4267fa83728" - integrity sha512-0q3naRVpENL0ReKHeNcwn/G7BDynp0DqZUckKyFtM9+hmpnPqgm8+8wbjiVZ0XNhq1wPQV28/Pb8Snh5adeUHA== +isbot@^5.1.22: + version "5.1.22" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.22.tgz#18a4a58bbfff6974ff7868dafea59907deb7b68d" + integrity sha512-RqCFY3cJy3c2y1I+rMn81cfzAR4XJwfPBC+M8kffUjbPzxApzyyv7Tbm1C/gXXq2dSCuD238pKFEWlQMTWsTFw== isexe@^2.0.0: version "2.0.0" From ebe26a7eb068da9ab3d5bfc1c5c51071559406b0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Feb 2025 11:21:57 -0600 Subject: [PATCH 004/395] Update version tag for development of next release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5be9ca9a44b..78fdd60f9f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dspace-angular", - "version": "7.6.3", + "version": "7.6.4-next", "scripts": { "ng": "ng", "config:watch": "nodemon", From 601850cb6afeb66dec157d49b7de481c4d57f3de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 02:18:00 +0000 Subject: [PATCH 005/395] Bump eslint-plugin-jsonc from 2.18.2 to 2.19.1 in the eslint group Bumps the eslint group with 1 update: [eslint-plugin-jsonc](https://github.com/ota-meshi/eslint-plugin-jsonc). Updates `eslint-plugin-jsonc` from 2.18.2 to 2.19.1 - [Release notes](https://github.com/ota-meshi/eslint-plugin-jsonc/releases) - [Changelog](https://github.com/ota-meshi/eslint-plugin-jsonc/blob/master/CHANGELOG.md) - [Commits](https://github.com/ota-meshi/eslint-plugin-jsonc/compare/v2.18.2...v2.19.1) --- updated-dependencies: - dependency-name: eslint-plugin-jsonc dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 78fdd60f9f8..8b9606e0a95 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,7 @@ "eslint-plugin-deprecation": "^1.5.0", "eslint-plugin-import": "^2.31.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-unused-imports": "^2.0.0", "express-static-gzip": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index 86a07c555ad..c2a45457ca4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5377,10 +5377,10 @@ eslint-plugin-jsdoc@^45.0.0: semver "^7.5.1" spdx-expression-parse "^3.0.1" -eslint-plugin-jsonc@^2.18.2: - version "2.18.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.18.2.tgz#893520feb35d343e7438e2fb57fad179ef3ff6aa" - integrity sha512-SDhJiSsWt3nItl/UuIv+ti4g3m4gpGkmnUJS9UWR3TrpyNsIcnJoBRD7Kof6cM4Rk3L0wrmY5Tm3z7ZPjR2uGg== +eslint-plugin-jsonc@^2.19.1: + version "2.19.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.19.1.tgz#aeedd7131d115b8c46f439a8855139837a0e2752" + integrity sha512-MmlAOaZK1+Lg7YoCZPGRjb88ZjT+ct/KTsvcsbZdBm+w8WMzGx+XEmexk0m40P1WV9G2rFV7X3klyRGRpFXEjA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" eslint-compat-utils "^0.6.0" From ed1aa37100bea2131945775d055ac966269e726c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 02:18:04 +0000 Subject: [PATCH 006/395] Bump cypress-axe from 1.5.0 to 1.6.0 in the testing group Bumps the testing group with 1 update: [cypress-axe](https://github.com/component-driven/cypress-axe). Updates `cypress-axe` from 1.5.0 to 1.6.0 - [Release notes](https://github.com/component-driven/cypress-axe/releases) - [Commits](https://github.com/component-driven/cypress-axe/compare/v1.5.0...v1.6.0) --- updated-dependencies: - dependency-name: cypress-axe dependency-type: direct:development update-type: version-update:semver-minor dependency-group: testing ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 78fdd60f9f8..1f6247fb452 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "cross-env": "^7.0.3", "csstype": "^3.1.3", "cypress": "^13.17.0", - "cypress-axe": "^1.5.0", + "cypress-axe": "^1.6.0", "deep-freeze": "0.0.1", "eslint": "^8.39.0", "eslint-plugin-deprecation": "^1.5.0", diff --git a/yarn.lock b/yarn.lock index 86a07c555ad..86f37f9d9f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4460,10 +4460,10 @@ custom-event@~1.0.0: resolved "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz" integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== -cypress-axe@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.5.0.tgz#95082734583da77b51ce9b7784e14a442016c7a1" - integrity sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ== +cypress-axe@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.6.0.tgz#2d70475e15356dd243ebcbfefd5719526f7ca9bc" + integrity sha512-C/ij50G8eebBrl/WsGT7E+T/SFyIsRZ3Epx9cRTLrPL9Y1GcxlQGFoAVdtSFWRrHSCWXq9HC6iJQMaI89O9yvQ== cypress@^13.17.0: version "13.17.0" From e0590109c10bf68c1de4e5ac4d1be5cb264a5e9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 02:18:27 +0000 Subject: [PATCH 007/395] Bump sass from 1.83.4 to 1.84.0 in the sass group Bumps the sass group with 1 update: [sass](https://github.com/sass/dart-sass). Updates `sass` from 1.83.4 to 1.84.0 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.83.4...1.84.0) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-minor dependency-group: sass ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 78fdd60f9f8..b14676981dc 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "react-copy-to-clipboard": "^5.1.0", "react-dom": "^16.14.0", "rimraf": "^3.0.2", - "sass": "~1.83.4", + "sass": "~1.84.0", "sass-loader": "^12.6.0", "sass-resources-loader": "^2.2.5", "ts-node": "^8.10.2", diff --git a/yarn.lock b/yarn.lock index 86a07c555ad..1a74d39ed53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10270,10 +10270,10 @@ sass@1.58.1: immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" -sass@^1.25.0, sass@~1.83.4: - version "1.83.4" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.4.tgz#5ccf60f43eb61eeec300b780b8dcb85f16eec6d1" - integrity sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA== +sass@^1.25.0, sass@~1.84.0: + version "1.84.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.84.0.tgz#da9154cbccb2d2eac7a9486091b6d9ba93ef5bad" + integrity sha512-XDAbhEPJRxi7H0SxrnOpiXFQoUJHwkR2u3Zc4el+fK/Tt5Hpzw5kkQ59qVDfvdaUq6gCrEZIbySFBM2T9DNKHg== dependencies: chokidar "^4.0.0" immutable "^5.0.2" From d336d8336178dcd4a5d822df188f2d47aca73599 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Thu, 13 Feb 2025 17:20:33 +0100 Subject: [PATCH 008/395] Cache-bust dynamic configuration files DSpace Angular's production server can modify the configuration it serves to CSR-only clients through YAML or environment variables. However, these files can remain cached in the browser and leave users with out-of-date configuration until the TTL runs out or the user does a "hard refresh". On build time this sort of problem is solved by saving the content hash as part of the path of each file (JS, CSS, ...) We introduce HashedFileMapping to bridge the same gap for dynamic content generated _after_ the server is built: - Files added to this mapping on the server are copied to a hashed path - A copy is injected into index.html, where clients can read it out and resolve the hashed paths - If a given path is not found in the mapping, the client will fall back to the original version (to prevent errors in development mode) With this mechanism we can ensure updates to config.json and similar files take immediate effect without losing the performance benefit of client-side caching. --- package.json | 1 + server.ts | 12 +- src/config/config.server.ts | 11 +- src/main.browser.ts | 5 +- .../hashed-file-mapping.browser.ts | 38 ++++++ .../hashed-file-mapping.server.ts | 115 ++++++++++++++++++ .../dynamic-hash/hashed-file-mapping.ts | 30 +++++ yarn.lock | 26 +++- 8 files changed, 229 insertions(+), 9 deletions(-) create mode 100644 src/modules/dynamic-hash/hashed-file-mapping.browser.ts create mode 100644 src/modules/dynamic-hash/hashed-file-mapping.server.ts create mode 100644 src/modules/dynamic-hash/hashed-file-mapping.ts diff --git a/package.json b/package.json index 719b13b23b6..d747d057782 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "ngx-pagination": "6.0.3", "ngx-sortablejs": "^11.1.0", "ngx-ui-switch": "^14.0.3", + "node-html-parser": "^7.0.1", "nouislider": "^14.6.3", "pem": "1.14.7", "prop-types": "^15.8.1", diff --git a/server.ts b/server.ts index 23327c2058e..cee6fc73916 100644 --- a/server.ts +++ b/server.ts @@ -52,6 +52,7 @@ import { ServerAppModule } from './src/main.server'; import { buildAppConfig } from './src/config/config.server'; import { APP_CONFIG, AppConfig } from './src/config/app-config.interface'; import { extendEnvironmentWithAppConfig } from './src/config/config.util'; +import { ServerHashedFileMapping } from './src/modules/dynamic-hash/hashed-file-mapping.server'; import { logStartupMessage } from './startup-message'; import { TOKENITEM } from './src/app/core/auth/models/auth-token-info.model'; @@ -67,7 +68,10 @@ const indexHtml = join(DIST_FOLDER, 'index.html'); const cookieParser = require('cookie-parser'); -const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/config.json')); +const configJson = join(DIST_FOLDER, 'assets/config.json'); +const hashedFileMapping = new ServerHashedFileMapping(DIST_FOLDER, 'index.html'); +const appConfig: AppConfig = buildAppConfig(configJson, hashedFileMapping); +hashedFileMapping.save(); // cache of SSR pages for known bots, only enabled in production mode let botCache: LRU; @@ -256,7 +260,7 @@ function ngApp(req, res) { */ function serverSideRender(req, res, sendToUser: boolean = true) { // Render the page via SSR (server side rendering) - res.render(indexHtml, { + res.render(hashedFileMapping.resolve(indexHtml), { req, res, preboot: environment.universal.preboot, @@ -298,7 +302,7 @@ function serverSideRender(req, res, sendToUser: boolean = true) { * @param res current response */ function clientSideRender(req, res) { - res.sendFile(indexHtml); + res.sendFile(hashedFileMapping.resolve(indexHtml)); } @@ -487,7 +491,7 @@ function saveToCache(req, page: any) { */ function hasNotSucceeded(statusCode) { const rgx = new RegExp(/^20+/); - return !rgx.test(statusCode) + return !rgx.test(statusCode); } function retrieveHeaders(response) { diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 65daede0a84..258c94567b6 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -2,6 +2,7 @@ import { red, blue, green, bold } from 'colors'; import { existsSync, readFileSync, writeFileSync } from 'fs'; import { load } from 'js-yaml'; import { join } from 'path'; +import { ServerHashedFileMapping } from '../modules/dynamic-hash/hashed-file-mapping.server'; import { AppConfig } from './app-config.interface'; import { Config } from './config.interface'; @@ -159,6 +160,7 @@ const buildBaseUrl = (config: ServerConfig): void => { ].join(''); }; + /** * Build app config with the following chain of override. * @@ -169,7 +171,7 @@ const buildBaseUrl = (config: ServerConfig): void => { * @param destConfigPath optional path to save config file * @returns app config */ -export const buildAppConfig = (destConfigPath?: string): AppConfig => { +export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFileMapping): AppConfig => { // start with default app config const appConfig: AppConfig = new DefaultAppConfig(); @@ -236,7 +238,12 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => { buildBaseUrl(appConfig.rest); if (isNotEmpty(destConfigPath)) { - writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); + const content = JSON.stringify(appConfig, null, 2); + + writeFileSync(destConfigPath, content); + if (mapping !== undefined) { + mapping.add(destConfigPath, content); + } console.log(`Angular ${bold('config.json')} file generated correctly at ${bold(destConfigPath)} \n`); } diff --git a/src/main.browser.ts b/src/main.browser.ts index 43b2ffbaf40..1b94d2de752 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -3,14 +3,15 @@ import 'reflect-metadata'; import 'core-js/es/reflect'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - import { BrowserAppModule } from './modules/app/browser-app.module'; import { environment } from './environments/environment'; import { AppConfig } from './config/app-config.interface'; import { extendEnvironmentWithAppConfig } from './config/config.util'; import { enableProdMode } from '@angular/core'; +import { BrowserHashedFileMapping } from './modules/dynamic-hash/hashed-file-mapping.browser'; +const hashedFileMapping = new BrowserHashedFileMapping(); const bootstrap = () => platformBrowserDynamic() .bootstrapModule(BrowserAppModule, {}); @@ -32,7 +33,7 @@ const main = () => { return bootstrap(); } else { // Configuration must be fetched explicitly - return fetch('assets/config.json') + return fetch(hashedFileMapping.resolve('assets/config.json')) .then((response) => response.json()) .then((appConfig: AppConfig) => { // extend environment with app config for browser when not prerendered diff --git a/src/modules/dynamic-hash/hashed-file-mapping.browser.ts b/src/modules/dynamic-hash/hashed-file-mapping.browser.ts new file mode 100644 index 00000000000..02df8d6d545 --- /dev/null +++ b/src/modules/dynamic-hash/hashed-file-mapping.browser.ts @@ -0,0 +1,38 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Injectable } from '@angular/core'; +import isObject from 'lodash/isObject'; +import isString from 'lodash/isString'; +import { hasValue } from '../../app/shared/empty.util'; +import { + HashedFileMapping, + ID, +} from './hashed-file-mapping'; + +/** + * Client-side implementation of {@link HashedFileMapping}. + * Reads out the mapping from index.html before the app is bootstrapped. + * Afterwards, {@link resolve} can be used to grab the latest file. + */ +@Injectable() +export class BrowserHashedFileMapping extends HashedFileMapping { + constructor() { + super(); + const element = document.querySelector(`script#${ID}`); + + if (hasValue(element?.textContent)) { + const mapping = JSON.parse(element.textContent); + + if (isObject(mapping)) { + Object.entries(mapping) + .filter(([key, value]) => isString(key) && isString(value)) + .forEach(([plainPath, hashPath]) => this.map.set(plainPath, hashPath)); + } + } + } +} diff --git a/src/modules/dynamic-hash/hashed-file-mapping.server.ts b/src/modules/dynamic-hash/hashed-file-mapping.server.ts new file mode 100644 index 00000000000..c709bab2782 --- /dev/null +++ b/src/modules/dynamic-hash/hashed-file-mapping.server.ts @@ -0,0 +1,115 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import crypto from 'crypto'; +import { + readFileSync, + rmSync, + writeFileSync, +} from 'fs'; +import glob from 'glob'; +import { parse } from 'node-html-parser'; +import { + extname, + join, + relative, +} from 'path'; +import zlib from 'zlib'; +import { + HashedFileMapping, + ID, +} from './hashed-file-mapping'; + +/** + * Server-side implementation of {@link HashedFileMapping}. + * Registers dynamically hashed files and stores them in index.html for the browser to use. + */ +export class ServerHashedFileMapping extends HashedFileMapping { + public readonly indexPath: string; + private readonly indexContent: string; + + constructor( + private readonly root: string, + file: string, + ) { + super(); + this.root = join(root, ''); + this.indexPath = join(root, file); + this.indexContent = readFileSync(this.indexPath).toString(); + } + + /** + * Add a new file to the mapping by an absolute path (within the root directory). + * If {@link content} is provided, the {@link path} itself does not have to exist. + * Otherwise, it is read out from the original path. + * The original path is never overwritten. + */ + add(path: string, content?: string, compress = false) { + if (content === undefined) { + readFileSync(path); + } + + // remove previous files + const ext = extname(path); + glob.GlobSync(path.replace(`${ext}`, `.*${ext}*`)) + .found + .forEach(p => rmSync(p)); + + // hash the content + const hash = crypto.createHash('md5') + .update(content) + .digest('hex'); + + // add the hash to the path + const hashPath = path.replace(`${ext}`, `.${hash}${ext}`); + + // store it in the mapping + this.map.set(path, hashPath); + + // write the file + writeFileSync(hashPath, content); + + if (compress) { + // write the file as .br + zlib.brotliCompress(content, (err, compressed) => { + if (err) { + throw new Error('Brotli compress failed'); + } else { + writeFileSync(hashPath + '.br', compressed); + } + }); + + // write the file as .gz + zlib.gzip(content, (err, compressed) => { + if (err) { + throw new Error('Gzip compress failed'); + } else { + writeFileSync(hashPath + '.gz', compressed); + } + }); + } + } + + /** + * Save the mapping as JSON in the index file. + * The updated index file itself is hashed as well, and must be sent {@link resolve}. + */ + save(): void { + const out = Array.from(this.map.entries()) + .reduce((object, [plain, hashed]) => { + object[relative(this.root, plain)] = relative(this.root, hashed); + return object; + }, {}); + + let root = parse(this.indexContent); + root.querySelector(`head > script#${ID}`)?.remove(); + root.querySelector('head') + .appendChild(`` as any); + + this.add(this.indexPath, root.toString()); + } +} diff --git a/src/modules/dynamic-hash/hashed-file-mapping.ts b/src/modules/dynamic-hash/hashed-file-mapping.ts new file mode 100644 index 00000000000..a3348b3fd1b --- /dev/null +++ b/src/modules/dynamic-hash/hashed-file-mapping.ts @@ -0,0 +1,30 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +export const ID = 'hashed-file-mapping'; + +/** + * A mapping of plain path to hashed path, used for cache-busting files that are created dynamically after DSpace has been built. + * The production server can register hashed files here and attach the map to index.html. + * The browser can then use it to resolve hashed files and circumvent the browser cache. + * + * This can ensure that e.g. configuration changes are consistently served to all CSR clients. + */ +export abstract class HashedFileMapping { + protected readonly map: Map = new Map(); + + /** + * Resolve a hashed path based on a plain path. + */ + resolve(plainPath: string): string { + if (this.map.has(plainPath)) { + const hashPath = this.map.get(plainPath); + return hashPath; + } + return plainPath; + } +} diff --git a/yarn.lock b/yarn.lock index 730966fcdb5..8f60a5d4127 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4372,6 +4372,17 @@ css-select@^4.2.0, css-select@^4.3.0: domutils "^2.8.0" nth-check "^2.0.1" +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz" @@ -4380,7 +4391,7 @@ css-vendor@^2.0.8: "@babel/runtime" "^7.8.3" is-in-browser "^1.0.2" -css-what@^6.0.1: +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -6184,6 +6195,11 @@ hdr-histogram-percentiles-obj@^3.0.0: resolved "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz" integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw== +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" @@ -8277,6 +8293,14 @@ node-gyp@^9.0.0: tar "^6.1.2" which "^2.0.2" +node-html-parser@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-7.0.1.tgz#e3056550bae48517ebf161a0b0638f4b0123dfe3" + integrity sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA== + dependencies: + css-select "^5.1.0" + he "1.2.0" + node-releases@^2.0.8: version "2.0.10" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" From 33721108399594f669e53556a22b0f48f88ec75d Mon Sep 17 00:00:00 2001 From: Pierre Lasou Date: Wed, 5 Feb 2025 16:19:49 -0500 Subject: [PATCH 009/395] Update fr.json5 (cherry picked from commit f721028d81c20e9ff10054b1f1970ba259b21b9b) --- src/assets/i18n/fr.json5 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index 8965d290aa0..e28edf97c30 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -5736,6 +5736,24 @@ // "submission.sections.general.sections_not_valid": "There are incomplete sections.", "submission.sections.general.sections_not_valid": "Sections incomplètes.", + //"submission.sections.identifiers.info": "The following identifiers will be created for your item:", + "submission.sections.identifiers.info": "Les identifiants uniques suivants seront attribués à votre document :", + + //"submission.sections.identifiers.no_handle": "No handles have been minted for this item.", + "submission.sections.identifiers.no_handle": "Aucun handle n'a été crée pour ce document.", + + //"submission.sections.identifiers.no_doi": "No DOIs have been minted for this item.", + "submission.sections.identifiers.no_doi": "Aucun DOI n'a été créé pour ce document.", + + //"submission.sections.identifiers.handle_label": "Handle: ", + "submission.sections.identifiers.handle_label": "Handle : ", + + //"submission.sections.identifiers.doi_label": "DOI: ", + "submission.sections.identifiers.doi_label": "DOI : ", + + //"submission.sections.identifiers.otherIdentifiers_label": "Other identifiers: ", + "submission.sections.identifiers.otherIdentifiers_label": "Autres identifiants : ", + // "submission.sections.submit.progressbar.accessCondition": "Item access conditions", "submission.sections.submit.progressbar.accessCondition": "Autorisations d'accès à l'Item", @@ -5757,6 +5775,9 @@ // "submission.sections.submit.progressbar.detect-duplicate": "Potential duplicates", "submission.sections.submit.progressbar.detect-duplicate": "Doublon(s) potentiel(s)", + //"submission.sections.submit.progressbar.identifiers": "Identifiers", + "submission.sections.submit.progressbar.identifiers": "Identifiants", + // "submission.sections.submit.progressbar.license": "Deposit license", "submission.sections.submit.progressbar.license": "Licence du dépôt", From 3daa1101166be8389b10d62079ef920519f37ce2 Mon Sep 17 00:00:00 2001 From: Carolyn Sullivan Date: Mon, 18 Nov 2024 15:21:25 -0500 Subject: [PATCH 010/395] Update fr.json5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajouté des traductions/noté quelques étiquettes sans traductions (cherry picked from commit 477ca5e71244bb7b7ef6e5c4eb9d6bb27f987b95) --- src/assets/i18n/fr.json5 | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index e28edf97c30..aaee62c5cbc 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -4144,6 +4144,27 @@ // "process.detail.back": "Back", "process.detail.back": "Retour", + //"process.detail.delete.body": "Are you sure you want to delete the current process?", + "process.detail.delete.body": "Êtes-vous sûr de vouloir supprimer ce processus?", + + //"process.detail.delete.button": "Delete process", + "process.detail.delete.button": "Supprimer le processus", + + //"process.detail.delete.cancel": "Cancel", + "process.detail.delete.cancel": "", + + //"process.detail.delete.confirm": "Delete process", + "process.detail.delete.confirm": "Supprimer le processus", + + //"process.detail.delete.error": "Something went wrong when deleting the process", + "process.detail.delete.error": "", + + //"process.detail.delete.header": "Delete process", + "process.detail.delete.header": "", + + //"process.detail.delete.success": "The process was successfully deleted.", + "process.detail.delete.success": "Le processus était supprimé", + // "process.detail.output": "Process Output", "process.detail.output": "Sortie du processus", @@ -4162,6 +4183,9 @@ // "process.detail.output-files.empty": "This process doesn't contain any output files", "process.detail.output-files.empty": "Ce processus ne contient pas de fichiers de sortie", + //"process.detail.refreshing": "Auto-refreshing…", + "process.detail.refreshing": "", + // "process.detail.script": "Script", "process.detail.script": "Script", From 9b0669f76cf577614ea1c34d7c6bc6ff92f8a654 Mon Sep 17 00:00:00 2001 From: Carolyn Sullivan Date: Fri, 14 Feb 2025 09:53:21 -0500 Subject: [PATCH 011/395] Update fr.json5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajouté les corrections faites par Pierre Lasou :) (cherry picked from commit e32d9feaf5197d11ee966fa079350c37d6e15229) --- src/assets/i18n/fr.json5 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index aaee62c5cbc..279506e4700 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -4151,19 +4151,19 @@ "process.detail.delete.button": "Supprimer le processus", //"process.detail.delete.cancel": "Cancel", - "process.detail.delete.cancel": "", + "process.detail.delete.cancel": "Annuler", //"process.detail.delete.confirm": "Delete process", "process.detail.delete.confirm": "Supprimer le processus", //"process.detail.delete.error": "Something went wrong when deleting the process", - "process.detail.delete.error": "", + "process.detail.delete.error": "Une erreur s'est produite lors de la suppression du processus", //"process.detail.delete.header": "Delete process", - "process.detail.delete.header": "", + "process.detail.delete.header": "Supprimer un processus", //"process.detail.delete.success": "The process was successfully deleted.", - "process.detail.delete.success": "Le processus était supprimé", + "process.detail.delete.success": "Le processus a bien été supprimé.", // "process.detail.output": "Process Output", "process.detail.output": "Sortie du processus", @@ -4184,7 +4184,7 @@ "process.detail.output-files.empty": "Ce processus ne contient pas de fichiers de sortie", //"process.detail.refreshing": "Auto-refreshing…", - "process.detail.refreshing": "", + "process.detail.refreshing": "Actualisation automatique...", // "process.detail.script": "Script", "process.detail.script": "Script", From e9fa2812a08534c45f595e8fb1938deb16c52798 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 02:52:11 +0000 Subject: [PATCH 012/395] Bump compression from 1.7.5 to 1.8.0 Bumps [compression](https://github.com/expressjs/compression) from 1.7.5 to 1.8.0. - [Release notes](https://github.com/expressjs/compression/releases) - [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/compression/compare/1.7.5...1.8.0) --- updated-dependencies: - dependency-name: compression dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ab4e54262f..7420cb4d202 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "cerialize": "0.1.18", "cli-progress": "^3.12.0", "colors": "^1.4.0", - "compression": "^1.7.5", + "compression": "^1.8.0", "cookie-parser": "1.4.7", "core-js": "^3.40.0", "date-fns": "^2.30.0", diff --git a/yarn.lock b/yarn.lock index 7983caea239..2f166423a5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4129,10 +4129,10 @@ compression-webpack-plugin@^9.2.0: schema-utils "^4.0.0" serialize-javascript "^6.0.0" -compression@^1.7.4, compression@^1.7.5: - version "1.7.5" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.5.tgz#fdd256c0a642e39e314c478f6c2cd654edd74c93" - integrity sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q== +compression@^1.7.4, compression@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.0.tgz#09420efc96e11a0f44f3a558de59e321364180f7" + integrity sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA== dependencies: bytes "3.1.2" compressible "~2.0.18" From fdc6e8aaec25ab11f9cee3bcb93e65ce1415e907 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Mon, 17 Feb 2025 12:33:37 +0100 Subject: [PATCH 013/395] Don't try to 'run' JSON on load The JSON mapping needs to be declared as a data block, otherwise browers may complain (nothing seems to really break though, except for Cypress) See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#any_other_value --- src/modules/dynamic-hash/hashed-file-mapping.server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/dynamic-hash/hashed-file-mapping.server.ts b/src/modules/dynamic-hash/hashed-file-mapping.server.ts index c709bab2782..41e83e6b19c 100644 --- a/src/modules/dynamic-hash/hashed-file-mapping.server.ts +++ b/src/modules/dynamic-hash/hashed-file-mapping.server.ts @@ -106,9 +106,9 @@ export class ServerHashedFileMapping extends HashedFileMapping { }, {}); let root = parse(this.indexContent); - root.querySelector(`head > script#${ID}`)?.remove(); + root.querySelector(`script#${ID}`)?.remove(); root.querySelector('head') - .appendChild(`` as any); + .appendChild(`` as any); this.add(this.indexPath, root.toString()); } From 2f235991057154531f6d244e4b5df59b17078dd6 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Mon, 17 Feb 2025 17:10:23 +0100 Subject: [PATCH 014/395] Add theme CSS to hashed file map --- server.ts | 1 + src/app/app.module.ts | 6 +++++ .../theme-support/theme.service.spec.ts | 13 +++++++++ src/app/shared/theme-support/theme.service.ts | 7 ++++- src/main.browser.ts | 2 +- .../hashed-file-mapping.browser.ts | 13 ++++++--- .../hashed-file-mapping.server.ts | 27 +++++++++++++++++-- 7 files changed, 62 insertions(+), 7 deletions(-) diff --git a/server.ts b/server.ts index cee6fc73916..e46f044923e 100644 --- a/server.ts +++ b/server.ts @@ -71,6 +71,7 @@ const cookieParser = require('cookie-parser'); const configJson = join(DIST_FOLDER, 'assets/config.json'); const hashedFileMapping = new ServerHashedFileMapping(DIST_FOLDER, 'index.html'); const appConfig: AppConfig = buildAppConfig(configJson, hashedFileMapping); +hashedFileMapping.addThemeStyles(); hashedFileMapping.save(); // cache of SSR pages for known bots, only enabled in production mode diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 89e361821ba..3ba227c134a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -10,6 +10,8 @@ import { MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/sto import { TranslateModule } from '@ngx-translate/core'; import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to'; import { DYNAMIC_MATCHER_PROVIDERS } from '@ng-dynamic-forms/core'; +import { HashedFileMapping } from '../modules/dynamic-hash/hashed-file-mapping'; +import { BrowserHashedFileMapping } from '../modules/dynamic-hash/hashed-file-mapping.browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @@ -103,6 +105,10 @@ const PROVIDERS = [ useClass: LogInterceptor, multi: true }, + { + provide: HashedFileMapping, + useClass: BrowserHashedFileMapping, + }, // register the dynamic matcher used by form. MUST be provided by the app module ...DYNAMIC_MATCHER_PROVIDERS, ]; diff --git a/src/app/shared/theme-support/theme.service.spec.ts b/src/app/shared/theme-support/theme.service.spec.ts index f56fb86cbc1..677bf065f6c 100644 --- a/src/app/shared/theme-support/theme.service.spec.ts +++ b/src/app/shared/theme-support/theme.service.spec.ts @@ -1,6 +1,7 @@ import { of as observableOf } from 'rxjs'; import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; +import { HashedFileMapping } from '../../../modules/dynamic-hash/hashed-file-mapping'; import { LinkService } from '../../core/cache/builders/link.service'; import { hot } from 'jasmine-marbles'; import { SetThemeAction } from './theme.actions'; @@ -46,6 +47,10 @@ class MockLinkService { } } +const mockHashedFileMapping = { + resolve: (path: string) => path, +}; + describe('ThemeService', () => { let themeService: ThemeService; let linkService: LinkService; @@ -96,6 +101,10 @@ describe('ThemeService', () => { provideMockActions(() => mockActions), { provide: DSpaceObjectDataService, useValue: mockDsoService }, { provide: Router, useValue: new RouterMock() }, + { + provide: HashedFileMapping, + useValue: mockHashedFileMapping, + }, ] }); @@ -393,6 +402,10 @@ describe('ThemeService', () => { provideMockStore({ initialState }), { provide: DSpaceObjectDataService, useValue: mockDsoService }, { provide: Router, useValue: new RouterMock() }, + { + provide: HashedFileMapping, + useValue: mockHashedFileMapping, + }, ] }); diff --git a/src/app/shared/theme-support/theme.service.ts b/src/app/shared/theme-support/theme.service.ts index 6d2939a5f88..8ffa3a2eb1c 100644 --- a/src/app/shared/theme-support/theme.service.ts +++ b/src/app/shared/theme-support/theme.service.ts @@ -1,6 +1,7 @@ import { Injectable, Inject, Injector } from '@angular/core'; import { Store, createFeatureSelector, createSelector, select } from '@ngrx/store'; import { BehaviorSubject, EMPTY, Observable, of as observableOf } from 'rxjs'; +import { HashedFileMapping } from '../../../modules/dynamic-hash/hashed-file-mapping'; import { ThemeState } from './theme.reducer'; import { SetThemeAction, ThemeActionTypes } from './theme.actions'; import { expand, filter, map, switchMap, take, toArray } from 'rxjs/operators'; @@ -57,6 +58,7 @@ export class ThemeService { @Inject(GET_THEME_CONFIG_FOR_FACTORY) private gtcf: (str) => ThemeConfig, private router: Router, @Inject(DOCUMENT) private document: any, + private hashedFileMapping: HashedFileMapping, ) { // Create objects from the theme configs in the environment file this.themes = environment.themes.map((themeConfig: ThemeConfig) => themeFactory(themeConfig, injector)); @@ -182,7 +184,10 @@ export class ThemeService { link.setAttribute('rel', 'stylesheet'); link.setAttribute('type', 'text/css'); link.setAttribute('class', 'theme-css'); - link.setAttribute('href', `${encodeURIComponent(themeName)}-theme.css`); + link.setAttribute( + 'href', + this.hashedFileMapping.resolve(`${encodeURIComponent(themeName)}-theme.css`), + ); // wait for the new css to download before removing the old one to prevent a // flash of unstyled content link.onload = () => { diff --git a/src/main.browser.ts b/src/main.browser.ts index 1b94d2de752..a73494cda2a 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -11,7 +11,7 @@ import { extendEnvironmentWithAppConfig } from './config/config.util'; import { enableProdMode } from '@angular/core'; import { BrowserHashedFileMapping } from './modules/dynamic-hash/hashed-file-mapping.browser'; -const hashedFileMapping = new BrowserHashedFileMapping(); +const hashedFileMapping = new BrowserHashedFileMapping(document); const bootstrap = () => platformBrowserDynamic() .bootstrapModule(BrowserAppModule, {}); diff --git a/src/modules/dynamic-hash/hashed-file-mapping.browser.ts b/src/modules/dynamic-hash/hashed-file-mapping.browser.ts index 02df8d6d545..576dc386314 100644 --- a/src/modules/dynamic-hash/hashed-file-mapping.browser.ts +++ b/src/modules/dynamic-hash/hashed-file-mapping.browser.ts @@ -5,7 +5,12 @@ * * http://www.dspace.org/license/ */ -import { Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { + Inject, + Injectable, + Optional, +} from '@angular/core'; import isObject from 'lodash/isObject'; import isString from 'lodash/isString'; import { hasValue } from '../../app/shared/empty.util'; @@ -21,9 +26,11 @@ import { */ @Injectable() export class BrowserHashedFileMapping extends HashedFileMapping { - constructor() { + constructor( + @Optional() @Inject(DOCUMENT) protected document: any, + ) { super(); - const element = document.querySelector(`script#${ID}`); + const element = document?.querySelector(`script#${ID}`); if (hasValue(element?.textContent)) { const mapping = JSON.parse(element.textContent); diff --git a/src/modules/dynamic-hash/hashed-file-mapping.server.ts b/src/modules/dynamic-hash/hashed-file-mapping.server.ts index 41e83e6b19c..822b3f5864f 100644 --- a/src/modules/dynamic-hash/hashed-file-mapping.server.ts +++ b/src/modules/dynamic-hash/hashed-file-mapping.server.ts @@ -10,6 +10,8 @@ import { readFileSync, rmSync, writeFileSync, + copyFileSync, + existsSync, } from 'fs'; import glob from 'glob'; import { parse } from 'node-html-parser'; @@ -48,9 +50,9 @@ export class ServerHashedFileMapping extends HashedFileMapping { * Otherwise, it is read out from the original path. * The original path is never overwritten. */ - add(path: string, content?: string, compress = false) { + add(path: string, content?: string, compress = false): string { if (content === undefined) { - readFileSync(path); + content = readFileSync(path).toString(); } // remove previous files @@ -92,6 +94,27 @@ export class ServerHashedFileMapping extends HashedFileMapping { } }); } + + return hashPath; + } + + addThemeStyles() { + glob.GlobSync(`${this.root}/*-theme.css`) + .found + .forEach(p => { + const hp = this.add(p); + this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.br'); + this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.gz'); + }); + } + + private ensureCompressedFilesAssumingUnchangedContent(path: string, hashedPath: string, compression: string) { + const compressedPath = `${path}${compression}`; + const compressedHashedPath = `${hashedPath}${compression}`; + + if (existsSync(compressedPath) && !existsSync(compressedHashedPath)) { + copyFileSync(compressedPath, compressedHashedPath); + } } /** From f8bcce43b50ec8d0997d0e97ce249c84fea31bcd Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Fri, 21 Feb 2025 13:19:01 +0100 Subject: [PATCH 015/395] 119602: Fix tests after merge --- .../models/array-group/dynamic-form-array.component.spec.ts | 3 +++ .../search-facet-option/search-facet-option.component.spec.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts index dc6565a0a4a..e5e86436c20 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts @@ -32,6 +32,8 @@ import { DynamicRowArrayModel } from '../ds-dynamic-row-array-model'; import { DsDynamicFormArrayComponent } from './dynamic-form-array.component'; import { UUIDService } from '../../../../../../core/shared/uuid.service'; import { TranslateLoaderMock } from '../../../../../mocks/translate-loader.mock'; +import { LiveRegionService } from '../../../../../live-region/live-region.service'; +import { getLiveRegionServiceStub } from '../../../../../live-region/live-region.service.stub'; describe('DsDynamicFormArrayComponent', () => { const translateServiceStub = { @@ -73,6 +75,7 @@ describe('DsDynamicFormArrayComponent', () => { { provide: SubmissionService, useValue: {} }, { provide: APP_CONFIG, useValue: environment }, { provide: UUIDService, useValue: uuidServiceStub }, + { provide: LiveRegionService, useValue: getLiveRegionServiceStub() }, ], }).overrideComponent(DsDynamicFormArrayComponent, { remove: { diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts index 5ddae92b853..153c804d317 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts @@ -20,6 +20,8 @@ import { PaginationService } from '../../../../../../core/pagination/pagination. import { PaginationServiceStub } from '../../../../../testing/pagination-service.stub'; import { ShortNumberPipe } from '../../../../../utils/short-number.pipe'; import { UUIDService } from '../../../../../../core/shared/uuid.service'; +import { LiveRegionService } from '../../../../../live-region/live-region.service'; +import { getLiveRegionServiceStub } from '../../../../../live-region/live-region.service.stub'; describe('SearchFacetOptionComponent', () => { let comp: SearchFacetOptionComponent; @@ -116,6 +118,7 @@ describe('SearchFacetOptionComponent', () => { } }, { provide: UUIDService, useClass: UUIDService }, + { provide: LiveRegionService, useValue: getLiveRegionServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(SearchFacetOptionComponent, { From 008fe0151be64abe6b1ea56125dc3c47cea76cd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 03:10:07 +0000 Subject: [PATCH 016/395] Bump @types/lodash from 4.17.15 to 4.17.16 Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.15 to 4.17.16. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ab4e54262f..ce4e4ed513b 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,7 @@ "@types/grecaptcha": "^3.0.9", "@types/jasmine": "~3.6.0", "@types/js-cookie": "2.2.6", - "@types/lodash": "^4.17.15", + "@types/lodash": "^4.17.16", "@types/node": "^14.18.63", "@types/sanitize-html": "^2.13.0", "@typescript-eslint/eslint-plugin": "^5.62.0", diff --git a/yarn.lock b/yarn.lock index 7983caea239..2bfdcb5829c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2539,10 +2539,10 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash@^4.17.15": - version "4.17.15" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.15.tgz#12d4af0ed17cc7600ce1f9980cec48fc17ad1e89" - integrity sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw== +"@types/lodash@^4.17.16": + version "4.17.16" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.16.tgz#94ae78fab4a38d73086e962d0b65c30d816bfb0a" + integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g== "@types/mime@*": version "3.0.1" From ac5c5c8a1480f95074374a30105bc5f10c0659b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 03:10:19 +0000 Subject: [PATCH 017/395] Bump core-js from 3.40.0 to 3.41.0 Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.40.0 to 3.41.0. - [Release notes](https://github.com/zloirock/core-js/releases) - [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/zloirock/core-js/commits/v3.41.0/packages/core-js) --- updated-dependencies: - dependency-name: core-js dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ab4e54262f..5b7f9ad8fc9 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "colors": "^1.4.0", "compression": "^1.7.5", "cookie-parser": "1.4.7", - "core-js": "^3.40.0", + "core-js": "^3.41.0", "date-fns": "^2.30.0", "date-fns-tz": "^1.3.7", "deepmerge": "^4.3.1", diff --git a/yarn.lock b/yarn.lock index 7983caea239..ac171124462 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4272,10 +4272,10 @@ core-js-compat@^3.25.1: dependencies: browserslist "^4.21.5" -core-js@^3.40.0: - version "3.40.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.40.0.tgz#2773f6b06877d8eda102fc42f828176437062476" - integrity sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ== +core-js@^3.41.0: + version "3.41.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.41.0.tgz#57714dafb8c751a6095d028a7428f1fb5834a776" + integrity sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA== core-util-is@1.0.2: version "1.0.2" From 8d7b4cd101c046e4044a0953a8d1d17fd2eaea50 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Tue, 25 Feb 2025 22:13:00 +0300 Subject: [PATCH 018/395] src/app/core: add citation_doi tag to head meta This is used by harvesters like Altmetric and was present in DSpace version 6 and previous. --- src/app/core/metadata/metadata.service.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 5659a75f0e5..864f96fbe16 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -161,6 +161,7 @@ export class MetadataService { this.setCitationKeywordsTag(); this.setCitationAbstractUrlTag(); + this.setCitationDoiTag(); this.setCitationPdfUrlTag(); this.setCitationPublisherTag(); @@ -294,6 +295,18 @@ export class MetadataService { } } + /** + * Add to the + */ + private setCitationDoiTag(): void { + if (this.currentObject.value instanceof Item) { + let doi = this.getMetaTagValue('dc.identifier.doi'); + if (hasValue(doi)) { + this.addMetaTag('citation_doi', doi); + } + } + } + /** * Add to the */ From 204556de20bfd2de99b4e234b0cd66708ea79f55 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Tue, 25 Feb 2025 22:14:03 +0300 Subject: [PATCH 019/395] src/app/core: remove unnecessary comment Remove commented out this.setCitationDOITag() since it is not used and we use camel case with this.setCitationDoiTag() now anyway. --- src/app/core/metadata/metadata.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 864f96fbe16..dff566bf7b8 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -174,7 +174,6 @@ export class MetadataService { // this.setCitationIssueTag(); // this.setCitationFirstPageTag(); // this.setCitationLastPageTag(); - // this.setCitationDOITag(); // this.setCitationPMIDTag(); // this.setCitationFullTextTag(); From c2528a9c1fc637c23122a04deaff9ab55e711f6c Mon Sep 17 00:00:00 2001 From: ana Date: Thu, 13 Mar 2025 09:51:17 +0100 Subject: [PATCH 020/395] Filters disappeared on reload --- .../search-filters/search-filters.component.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index b491f211770..831c32d76cd 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -88,14 +88,16 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe( - tap(() => this.filtersWithComputedVisibility = 0), - map((filters) => { - Object.keys(filters).forEach((f) => filters[f] = null); - return filters; - }) - ); - this.searchLink = this.getSearchLink(); + this.router.events.subscribe(() => { + this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe( + tap(() => this.filtersWithComputedVisibility = 0), + map((filters) => { + Object.keys(filters).forEach((f) => filters[f] = null); + return filters; + }) + ); + this.searchLink = this.getSearchLink(); + }); } /** From ce4dd3aa3254be96a8e28b1d5090b2cdbce001b5 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Mon, 24 Feb 2025 13:21:00 +0100 Subject: [PATCH 021/395] Simplify testing by making mapping optional Mainly for 8.x and beyond, where ThemeService is injected more often ~ standalone components --- src/app/shared/theme-support/theme.service.spec.ts | 12 ------------ src/app/shared/theme-support/theme.service.ts | 12 +++++++++--- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/app/shared/theme-support/theme.service.spec.ts b/src/app/shared/theme-support/theme.service.spec.ts index 677bf065f6c..1eccb5088dd 100644 --- a/src/app/shared/theme-support/theme.service.spec.ts +++ b/src/app/shared/theme-support/theme.service.spec.ts @@ -47,10 +47,6 @@ class MockLinkService { } } -const mockHashedFileMapping = { - resolve: (path: string) => path, -}; - describe('ThemeService', () => { let themeService: ThemeService; let linkService: LinkService; @@ -101,10 +97,6 @@ describe('ThemeService', () => { provideMockActions(() => mockActions), { provide: DSpaceObjectDataService, useValue: mockDsoService }, { provide: Router, useValue: new RouterMock() }, - { - provide: HashedFileMapping, - useValue: mockHashedFileMapping, - }, ] }); @@ -402,10 +394,6 @@ describe('ThemeService', () => { provideMockStore({ initialState }), { provide: DSpaceObjectDataService, useValue: mockDsoService }, { provide: Router, useValue: new RouterMock() }, - { - provide: HashedFileMapping, - useValue: mockHashedFileMapping, - }, ] }); diff --git a/src/app/shared/theme-support/theme.service.ts b/src/app/shared/theme-support/theme.service.ts index 8ffa3a2eb1c..0fb957b7260 100644 --- a/src/app/shared/theme-support/theme.service.ts +++ b/src/app/shared/theme-support/theme.service.ts @@ -1,4 +1,9 @@ -import { Injectable, Inject, Injector } from '@angular/core'; +import { + Injectable, + Inject, + Injector, + Optional, +} from '@angular/core'; import { Store, createFeatureSelector, createSelector, select } from '@ngrx/store'; import { BehaviorSubject, EMPTY, Observable, of as observableOf } from 'rxjs'; import { HashedFileMapping } from '../../../modules/dynamic-hash/hashed-file-mapping'; @@ -58,7 +63,7 @@ export class ThemeService { @Inject(GET_THEME_CONFIG_FOR_FACTORY) private gtcf: (str) => ThemeConfig, private router: Router, @Inject(DOCUMENT) private document: any, - private hashedFileMapping: HashedFileMapping, + @Optional() private hashedFileMapping: HashedFileMapping, ) { // Create objects from the theme configs in the environment file this.themes = environment.themes.map((themeConfig: ThemeConfig) => themeFactory(themeConfig, injector)); @@ -181,12 +186,13 @@ export class ThemeService { // automatically updated if we add nodes later const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css')); const link = this.document.createElement('link'); + const themeCSS = `${encodeURIComponent(themeName)}-theme.css`; link.setAttribute('rel', 'stylesheet'); link.setAttribute('type', 'text/css'); link.setAttribute('class', 'theme-css'); link.setAttribute( 'href', - this.hashedFileMapping.resolve(`${encodeURIComponent(themeName)}-theme.css`), + this.hashedFileMapping?.resolve(themeCSS) ?? themeCSS, ); // wait for the new css to download before removing the old one to prevent a // flash of unstyled content From 928e932b06eb1eb78364ebd940ae7d36430ac3aa Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Mon, 17 Mar 2025 15:29:53 +0100 Subject: [PATCH 022/395] Optionally preload hashed files Both the configuration and theme CSS are known to be needed for every render. We can save some time by retrieving them as soon as the HTML is read. --- server.ts | 2 +- src/config/config.server.ts | 8 ++- .../hashed-file-mapping.server.ts | 51 +++++++++++++++---- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/server.ts b/server.ts index e46f044923e..3083b9ed4f8 100644 --- a/server.ts +++ b/server.ts @@ -71,7 +71,7 @@ const cookieParser = require('cookie-parser'); const configJson = join(DIST_FOLDER, 'assets/config.json'); const hashedFileMapping = new ServerHashedFileMapping(DIST_FOLDER, 'index.html'); const appConfig: AppConfig = buildAppConfig(configJson, hashedFileMapping); -hashedFileMapping.addThemeStyles(); +hashedFileMapping.addThemeStyles(appConfig.themes); hashedFileMapping.save(); // cache of SSR pages for known bots, only enabled in production mode diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 258c94567b6..231affcfc71 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -2,9 +2,11 @@ import { red, blue, green, bold } from 'colors'; import { existsSync, readFileSync, writeFileSync } from 'fs'; import { load } from 'js-yaml'; import { join } from 'path'; +import { environment } from '../environments/environment'; import { ServerHashedFileMapping } from '../modules/dynamic-hash/hashed-file-mapping.server'; import { AppConfig } from './app-config.interface'; +import { BuildConfig } from './build-config.interface'; import { Config } from './config.interface'; import { DefaultAppConfig } from './default-app-config'; import { ServerConfig } from './server-config.interface'; @@ -173,7 +175,7 @@ const buildBaseUrl = (config: ServerConfig): void => { */ export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFileMapping): AppConfig => { // start with default app config - const appConfig: AppConfig = new DefaultAppConfig(); + const appConfig: BuildConfig = new DefaultAppConfig() as BuildConfig; // determine which dist app config by environment const env = getEnvironment(); @@ -243,6 +245,10 @@ export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFi writeFileSync(destConfigPath, content); if (mapping !== undefined) { mapping.add(destConfigPath, content); + if (!appConfig.universal.preboot) { + // If we're serving for CSR we can retrieve the configuration before JS is loaded/executed + mapping.addHeadLink(destConfigPath, 'preload', 'fetch', 'anonymous'); + } } console.log(`Angular ${bold('config.json')} file generated correctly at ${bold(destConfigPath)} \n`); diff --git a/src/modules/dynamic-hash/hashed-file-mapping.server.ts b/src/modules/dynamic-hash/hashed-file-mapping.server.ts index 822b3f5864f..8c184f110c5 100644 --- a/src/modules/dynamic-hash/hashed-file-mapping.server.ts +++ b/src/modules/dynamic-hash/hashed-file-mapping.server.ts @@ -7,11 +7,11 @@ */ import crypto from 'crypto'; import { + copyFileSync, + existsSync, readFileSync, rmSync, writeFileSync, - copyFileSync, - existsSync, } from 'fs'; import glob from 'glob'; import { parse } from 'node-html-parser'; @@ -21,6 +21,8 @@ import { relative, } from 'path'; import zlib from 'zlib'; +import { hasValue } from '../../app/shared/empty.util'; +import { ThemeConfig } from '../../config/theme.model'; import { HashedFileMapping, ID, @@ -34,6 +36,8 @@ export class ServerHashedFileMapping extends HashedFileMapping { public readonly indexPath: string; private readonly indexContent: string; + protected readonly headLinks: Set = new Set(); + constructor( private readonly root: string, file: string, @@ -98,14 +102,36 @@ export class ServerHashedFileMapping extends HashedFileMapping { return hashPath; } - addThemeStyles() { - glob.GlobSync(`${this.root}/*-theme.css`) - .found - .forEach(p => { - const hp = this.add(p); - this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.br'); - this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.gz'); - }); + /** + * Add CSS for all configured themes to the mapping + * @param themeConfigurations + */ + addThemeStyles(themeConfigurations: ThemeConfig[]) { + for (const themeConfiguration of themeConfigurations) { + const p = `${this.root}/${themeConfiguration.name}-theme.css`; + const hp = this.add(p); + + // We know this CSS is likely needed, so wecan avoid a FOUC by retrieving it in advance + // Angular does the same for global styles, but doesn't "know" about out themes + this.addHeadLink(p, 'prefetch', 'style'); + + this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.br'); + this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.gz'); + } + } + + /** + * Include a head link for a given resource to the index HTML. + */ + addHeadLink(path: string, rel: string, as: string, crossorigin?: string) { + const href = relative(this.root, this.resolve(path)); + + if (hasValue(crossorigin)) { + this.headLinks.add(``); + + } else { + this.headLinks.add(``); + } } private ensureCompressedFilesAssumingUnchangedContent(path: string, hashedPath: string, compression: string) { @@ -133,6 +159,11 @@ export class ServerHashedFileMapping extends HashedFileMapping { root.querySelector('head') .appendChild(`` as any); + for (const headLink of this.headLinks) { + root.querySelector('head') + .appendChild(headLink as any); + } + this.add(this.indexPath, root.toString()); } } From c6bf2b7355c3c7ba493c3b2688709a9125740517 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Tue, 18 Mar 2025 17:32:50 +0100 Subject: [PATCH 023/395] Don't hash or cache the index file This didn't work well in one specific case: requests without a path (e.g. https://demo.dspace.org) Such requests would result in a 304 redirect directly to index.html, losing the hashed file mapping and causing it to get cached. Then it could remain in the cache across rebuilds. Solutions: - Don't try to hash index.html, but modify it in place - Introduce configuration to disable caching for specific static files and apply this to index.html to prevent similar problems - Don't let browsers cache index.html when it's served for CSR under another path --- config/config.example.yml | 3 ++ server.ts | 14 ++++++-- .../theme-support/theme.service.spec.ts | 1 - src/config/cache-config.interface.ts | 2 ++ src/config/config.server.ts | 12 ++++--- src/config/default-app-config.ts | 4 +++ src/environments/environment.test.ts | 4 +++ .../hashed-file-mapping.server.ts | 36 +++++++++++++------ 8 files changed, 58 insertions(+), 18 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index ea38303fa36..1e4397df8e7 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -38,6 +38,9 @@ cache: # NOTE: When updates are made to compiled *.js files, it will automatically bypass this browser cache, because # all compiled *.js files include a unique hash in their name which updates when content is modified. control: max-age=604800 # revalidate browser + # These static files should not be cached (paths relative to dist/browser, including the leading slash) + noCacheFiles: + - '/index.html' autoSync: defaultTime: 0 maxBufferSize: 100 diff --git a/server.ts b/server.ts index 3083b9ed4f8..827cb455281 100644 --- a/server.ts +++ b/server.ts @@ -261,7 +261,7 @@ function ngApp(req, res) { */ function serverSideRender(req, res, sendToUser: boolean = true) { // Render the page via SSR (server side rendering) - res.render(hashedFileMapping.resolve(indexHtml), { + res.render(indexHtml, { req, res, preboot: environment.universal.preboot, @@ -303,7 +303,11 @@ function serverSideRender(req, res, sendToUser: boolean = true) { * @param res current response */ function clientSideRender(req, res) { - res.sendFile(hashedFileMapping.resolve(indexHtml)); + res.sendFile(indexHtml, { + headers: { + 'Cache-Control': 'no-cache, no-store', + }, + }); } @@ -314,7 +318,11 @@ function clientSideRender(req, res) { */ function addCacheControl(req, res, next) { // instruct browser to revalidate - res.header('Cache-Control', environment.cache.control || 'max-age=604800'); + if (environment.cache.noCacheFiles.includes(req.originalUrl)) { + res.header('Cache-Control', 'no-cache, no-store'); + } else { + res.header('Cache-Control', environment.cache.control || 'max-age=604800'); + } next(); } diff --git a/src/app/shared/theme-support/theme.service.spec.ts b/src/app/shared/theme-support/theme.service.spec.ts index 1eccb5088dd..f56fb86cbc1 100644 --- a/src/app/shared/theme-support/theme.service.spec.ts +++ b/src/app/shared/theme-support/theme.service.spec.ts @@ -1,7 +1,6 @@ import { of as observableOf } from 'rxjs'; import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; -import { HashedFileMapping } from '../../../modules/dynamic-hash/hashed-file-mapping'; import { LinkService } from '../../core/cache/builders/link.service'; import { hot } from 'jasmine-marbles'; import { SetThemeAction } from './theme.actions'; diff --git a/src/config/cache-config.interface.ts b/src/config/cache-config.interface.ts index 73520c95ea1..fd7719a037c 100644 --- a/src/config/cache-config.interface.ts +++ b/src/config/cache-config.interface.ts @@ -7,6 +7,8 @@ export interface CacheConfig extends Config { }; // Cache-Control HTTP Header control: string; + // These static files should not be cached (paths relative to dist/browser, including the leading slash) + noCacheFiles: string[] autoSync: AutoSyncConfig; // In-memory caches of server-side rendered (SSR) content. These caches can be used to limit the frequency // of re-generating SSR pages to improve performance. diff --git a/src/config/config.server.ts b/src/config/config.server.ts index 231affcfc71..c74a5ef9c8b 100644 --- a/src/config/config.server.ts +++ b/src/config/config.server.ts @@ -2,7 +2,6 @@ import { red, blue, green, bold } from 'colors'; import { existsSync, readFileSync, writeFileSync } from 'fs'; import { load } from 'js-yaml'; import { join } from 'path'; -import { environment } from '../environments/environment'; import { ServerHashedFileMapping } from '../modules/dynamic-hash/hashed-file-mapping.server'; import { AppConfig } from './app-config.interface'; @@ -175,7 +174,7 @@ const buildBaseUrl = (config: ServerConfig): void => { */ export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFileMapping): AppConfig => { // start with default app config - const appConfig: BuildConfig = new DefaultAppConfig() as BuildConfig; + const appConfig: AppConfig = new DefaultAppConfig(); // determine which dist app config by environment const env = getEnvironment(); @@ -245,9 +244,14 @@ export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFi writeFileSync(destConfigPath, content); if (mapping !== undefined) { mapping.add(destConfigPath, content); - if (!appConfig.universal.preboot) { + if (!(appConfig as BuildConfig).universal?.preboot) { // If we're serving for CSR we can retrieve the configuration before JS is loaded/executed - mapping.addHeadLink(destConfigPath, 'preload', 'fetch', 'anonymous'); + mapping.addHeadLink({ + path: destConfigPath, + rel: 'preload', + as: 'fetch', + crossorigin: 'anonymous', + }); } } diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index a6e9e092e46..b2eb51ec9bf 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -70,6 +70,10 @@ export class DefaultAppConfig implements AppConfig { }, // Cache-Control HTTP Header control: 'max-age=604800', // revalidate browser + // These static files should not be cached (paths relative to dist/browser, including the leading slash) + noCacheFiles: [ + '/index.html', // see https://web.dev/articles/http-cache#unversioned-urls + ], autoSync: { defaultTime: 0, maxBufferSize: 100, diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index cb9d2c71303..ecfd520cb18 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -51,6 +51,10 @@ export const environment: BuildConfig = { }, // msToLive: 1000, // 15 minutes control: 'max-age=60', + // These static files should not be cached (paths relative to dist/browser, including the leading slash) + noCacheFiles: [ + '/index.html', // see https://web.dev/articles/http-cache#unversioned-urls + ], autoSync: { defaultTime: 0, maxBufferSize: 100, diff --git a/src/modules/dynamic-hash/hashed-file-mapping.server.ts b/src/modules/dynamic-hash/hashed-file-mapping.server.ts index 8c184f110c5..b922fc90661 100644 --- a/src/modules/dynamic-hash/hashed-file-mapping.server.ts +++ b/src/modules/dynamic-hash/hashed-file-mapping.server.ts @@ -28,6 +28,15 @@ import { ID, } from './hashed-file-mapping'; +const HEAD_LINK_CLASS = 'hfm'; + +interface HeadLink { + path: string; + rel: string; + as: string; + crossorigin?: string; +} + /** * Server-side implementation of {@link HashedFileMapping}. * Registers dynamically hashed files and stores them in index.html for the browser to use. @@ -36,7 +45,7 @@ export class ServerHashedFileMapping extends HashedFileMapping { public readonly indexPath: string; private readonly indexContent: string; - protected readonly headLinks: Set = new Set(); + protected readonly headLinks: Set = new Set(); constructor( private readonly root: string, @@ -113,7 +122,11 @@ export class ServerHashedFileMapping extends HashedFileMapping { // We know this CSS is likely needed, so wecan avoid a FOUC by retrieving it in advance // Angular does the same for global styles, but doesn't "know" about out themes - this.addHeadLink(p, 'prefetch', 'style'); + this.addHeadLink({ + path: p, + rel: 'prefetch', + as: 'style', + }); this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.br'); this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.gz'); @@ -123,14 +136,17 @@ export class ServerHashedFileMapping extends HashedFileMapping { /** * Include a head link for a given resource to the index HTML. */ - addHeadLink(path: string, rel: string, as: string, crossorigin?: string) { - const href = relative(this.root, this.resolve(path)); + addHeadLink(headLink: HeadLink) { + this.headLinks.add(headLink); + } - if (hasValue(crossorigin)) { - this.headLinks.add(``); + private renderHeadLink(link: HeadLink): string { + const href = relative(this.root, this.resolve(link.path)); + if (hasValue(link.crossorigin)) { + return ``; } else { - this.headLinks.add(``); + return ``; } } @@ -155,15 +171,15 @@ export class ServerHashedFileMapping extends HashedFileMapping { }, {}); let root = parse(this.indexContent); - root.querySelector(`script#${ID}`)?.remove(); + root.querySelectorAll(`script#${ID}, link.${HEAD_LINK_CLASS}`)?.forEach(e => e.remove()); root.querySelector('head') .appendChild(`` as any); for (const headLink of this.headLinks) { root.querySelector('head') - .appendChild(headLink as any); + .appendChild(this.renderHeadLink(headLink) as any); } - this.add(this.indexPath, root.toString()); + writeFileSync(this.indexPath, root.toString()); } } From 6c11621d91e5ea4001c739bea086d7b67326f7c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 02:47:30 +0000 Subject: [PATCH 024/395] Bump isbot from 5.1.22 to 5.1.25 Bumps [isbot](https://github.com/omrilotan/isbot) from 5.1.22 to 5.1.25. - [Changelog](https://github.com/omrilotan/isbot/blob/main/CHANGELOG.md) - [Commits](https://github.com/omrilotan/isbot/compare/v5.1.22...v5.1.25) --- updated-dependencies: - dependency-name: isbot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ab4e54262f..f62fb65acc5 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "filesize": "^6.1.0", "http-proxy-middleware": "^2.0.7", "http-terminator": "^3.2.0", - "isbot": "^5.1.22", + "isbot": "^5.1.25", "js-cookie": "2.2.1", "js-yaml": "^4.1.0", "json5": "^2.2.3", diff --git a/yarn.lock b/yarn.lock index 7983caea239..421804eadc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7080,10 +7080,10 @@ isbinaryfile@^4.0.8: resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz" integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== -isbot@^5.1.22: - version "5.1.22" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.22.tgz#18a4a58bbfff6974ff7868dafea59907deb7b68d" - integrity sha512-RqCFY3cJy3c2y1I+rMn81cfzAR4XJwfPBC+M8kffUjbPzxApzyyv7Tbm1C/gXXq2dSCuD238pKFEWlQMTWsTFw== +isbot@^5.1.25: + version "5.1.25" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.25.tgz#3f08f13a773a9cd86f7e498094f36974de0c151d" + integrity sha512-mqU76fmT7cpGG0JX1EzhCZIC+xovpH6TD2SAK18alonk0RG/RgChpGduJTYzRaq9a0COoFA99M9JVtEUOcScIw== isexe@^2.0.0: version "2.0.0" From fa6b8cc21d27e070666ac764c03671753fb8f73f Mon Sep 17 00:00:00 2001 From: abhinav Date: Fri, 21 Mar 2025 13:34:04 +0100 Subject: [PATCH 025/395] 129641: Fix Export button is enabled wrongly This fixes the export button being enabled when the second step is not active in bulk access edit. The tests have been updated accordingly --- .../bulk-access/bulk-access.component.spec.ts | 45 ++++++++++++++----- .../bulk-access/bulk-access.component.ts | 4 +- .../bulk-access-settings.component.ts | 4 ++ .../access-control-array-form.component.ts | 4 ++ ...access-control-form-container.component.ts | 4 ++ 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/app/access-control/bulk-access/bulk-access.component.spec.ts b/src/app/access-control/bulk-access/bulk-access.component.spec.ts index e9b253147dc..4a545dc3dd9 100644 --- a/src/app/access-control/bulk-access/bulk-access.component.spec.ts +++ b/src/app/access-control/bulk-access/bulk-access.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { NO_ERRORS_SCHEMA, Component } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { of } from 'rxjs'; @@ -57,10 +57,15 @@ describe('BulkAccessComponent', () => { 'file': { } }; - const mockSettings: any = jasmine.createSpyObj('AccessControlFormContainerComponent', { - getValue: jasmine.createSpy('getValue'), - reset: jasmine.createSpy('reset') - }); + @Component({ + selector: 'ds-bulk-access-settings', + template: '' + }) + class MockBulkAccessSettingsComponent { + isFormValid = jasmine.createSpy('isFormValid').and.returnValue(false); + getValue = jasmine.createSpy('getValue'); + reset = jasmine.createSpy('reset'); + } const selection: any[] = [{ indexableObject: { uuid: '1234' } }, { indexableObject: { uuid: '5678' } }]; const selectableListState: SelectableListState = { id: 'test', selection }; const expectedIdList = ['1234', '5678']; @@ -73,7 +78,10 @@ describe('BulkAccessComponent', () => { RouterTestingModule, TranslateModule.forRoot() ], - declarations: [ BulkAccessComponent ], + declarations: [ + BulkAccessComponent, + MockBulkAccessSettingsComponent, + ], providers: [ { provide: BulkAccessControlService, useValue: bulkAccessControlServiceMock }, { provide: NotificationsService, useValue: NotificationsServiceStub }, @@ -102,7 +110,6 @@ describe('BulkAccessComponent', () => { (component as any).selectableListService.getSelectableList.and.returnValue(of(selectableListStateEmpty)); fixture.detectChanges(); - component.settings = mockSettings; }); it('should create', () => { @@ -119,13 +126,12 @@ describe('BulkAccessComponent', () => { }); - describe('when there are elements selected', () => { + describe('when there are elements selected and step two form is invalid', () => { beforeEach(() => { (component as any).selectableListService.getSelectableList.and.returnValue(of(selectableListState)); fixture.detectChanges(); - component.settings = mockSettings; }); it('should create', () => { @@ -136,9 +142,9 @@ describe('BulkAccessComponent', () => { expect(component.objectsSelected$.value).toEqual(expectedIdList); }); - it('should enable the execute button when there are objects selected', () => { + it('should not enable the execute button when there are objects selected and step two form is invalid', () => { component.objectsSelected$.next(['1234']); - expect(component.canExport()).toBe(true); + expect(component.canExport()).toBe(false); }); it('should call the settings reset method when reset is called', () => { @@ -146,6 +152,23 @@ describe('BulkAccessComponent', () => { expect(component.settings.reset).toHaveBeenCalled(); }); + + }); + + describe('when there are elements selectedted and the step two form is valid', () => { + + beforeEach(() => { + + (component as any).selectableListService.getSelectableList.and.returnValue(of(selectableListState)); + fixture.detectChanges(); + (component as any).settings.isFormValid.and.returnValue(true); + }); + + it('should enable the execute button when there are objects selected and step two form is valid', () => { + component.objectsSelected$.next(['1234']); + expect(component.canExport()).toBe(true); + }); + it('should call the bulkAccessControlService executeScript method when submit is called', () => { (component.settings as any).getValue.and.returnValue(mockFormState); bulkAccessControlService.createPayloadFile.and.returnValue(mockFile); diff --git a/src/app/access-control/bulk-access/bulk-access.component.ts b/src/app/access-control/bulk-access/bulk-access.component.ts index 04724614cb6..bdea3d5cbee 100644 --- a/src/app/access-control/bulk-access/bulk-access.component.ts +++ b/src/app/access-control/bulk-access/bulk-access.component.ts @@ -37,7 +37,7 @@ export class BulkAccessComponent implements OnInit { constructor( private bulkAccessControlService: BulkAccessControlService, - private selectableListService: SelectableListService + private selectableListService: SelectableListService, ) { } @@ -51,7 +51,7 @@ export class BulkAccessComponent implements OnInit { } canExport(): boolean { - return this.objectsSelected$.value?.length > 0; + return this.objectsSelected$.value?.length > 0 && this.settings?.isFormValid(); } /** diff --git a/src/app/access-control/bulk-access/settings/bulk-access-settings.component.ts b/src/app/access-control/bulk-access/settings/bulk-access-settings.component.ts index eecc0162451..5d1070893c4 100644 --- a/src/app/access-control/bulk-access/settings/bulk-access-settings.component.ts +++ b/src/app/access-control/bulk-access/settings/bulk-access-settings.component.ts @@ -31,4 +31,8 @@ export class BulkAccessSettingsComponent { this.controlForm.reset(); } + isFormValid() { + return this.controlForm.isValid(); + } + } diff --git a/src/app/shared/access-control-form-container/access-control-array-form/access-control-array-form.component.ts b/src/app/shared/access-control-form-container/access-control-array-form/access-control-array-form.component.ts index 227de596ff2..f91bae78586 100644 --- a/src/app/shared/access-control-form-container/access-control-array-form/access-control-array-form.component.ts +++ b/src/app/shared/access-control-form-container/access-control-array-form/access-control-array-form.component.ts @@ -119,6 +119,10 @@ export class AccessControlArrayFormComponent implements OnInit { return item.id; } + isValid() { + return this.ngForm.valid; + } + } diff --git a/src/app/shared/access-control-form-container/access-control-form-container.component.ts b/src/app/shared/access-control-form-container/access-control-form-container.component.ts index cddd1b1a29d..ad3a3c0052f 100644 --- a/src/app/shared/access-control-form-container/access-control-form-container.component.ts +++ b/src/app/shared/access-control-form-container/access-control-form-container.component.ts @@ -156,5 +156,9 @@ export class AccessControlFormContainerComponent impleme this.selectableListService.deselectAll(ITEM_ACCESS_CONTROL_SELECT_BITSTREAMS_LIST_ID); } + isValid() { + return this.bitstreamAccessCmp.isValid() || this.itemAccessCmp.isValid(); + } + } From ecc03343b7fb6640b3f59e076d824edbf0f2d527 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 02:43:43 +0000 Subject: [PATCH 026/395] Bump @babel/runtime from 7.26.7 to 7.27.0 Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.7 to 7.27.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime) --- updated-dependencies: - dependency-name: "@babel/runtime" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ab4e54262f..b992942b07d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@angular/platform-browser-dynamic": "^15.2.10", "@angular/platform-server": "^15.2.10", "@angular/router": "^15.2.10", - "@babel/runtime": "7.26.7", + "@babel/runtime": "7.27.0", "@kolkov/ngx-gallery": "^2.0.1", "@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-dynamic-forms/core": "^15.0.0", diff --git a/yarn.lock b/yarn.lock index 7983caea239..f0df680c15e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1352,10 +1352,10 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@7.26.7", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" - integrity sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ== +"@babel/runtime@7.27.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762" + integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== dependencies: regenerator-runtime "^0.14.0" From 7dd6ab79ffcb26a15b825b5eda46ce1aebc93370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Mon, 31 Mar 2025 09:14:32 +0200 Subject: [PATCH 027/395] Store the state of the computed filters --- .../search-filters.component.html | 10 +- .../search-filters.component.ts | 131 +++++++++++++++++- 2 files changed, 131 insertions(+), 10 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filters.component.html b/src/app/shared/search/search-filters/search-filters.component.html index e1e54c683dd..bb020f85b18 100644 --- a/src/app/shared/search/search-filters/search-filters.component.html +++ b/src/app/shared/search/search-filters/search-filters.component.html @@ -1,13 +1,13 @@

{{"search.filters.head" | translate}}

-
- -
+
+ +
- + {{"search.filters.reset" | translate}} diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 831c32d76cd..b23a6452055 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -2,7 +2,7 @@ import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; +import { map, filter } from 'rxjs/operators'; import { SearchService } from '../../../core/shared/search/search.service'; import { RemoteData } from '../../../core/data/remote-data'; @@ -62,9 +62,16 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { searchLink: string; /** - * Filters for which visibility has been computed + * Keeps track of the filters computed for each configuration during the current rendering cycle + * This array stores objects with configuration identifier and number of computed filters */ - filtersWithComputedVisibility = 0; + private currentFiltersComputed = []; + + /** + * Stores the final count of computed filters for each configuration + * Used to determine when all filters for a configuration have been processed + */ + private finalFiltersComputed = []; subs = []; defaultFilterCount: number; @@ -90,7 +97,6 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { ngOnInit(): void { this.router.events.subscribe(() => { this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe( - tap(() => this.filtersWithComputedVisibility = 0), map((filters) => { Object.keys(filters).forEach((f) => filters[f] = null); return filters; @@ -127,7 +133,122 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { countFiltersWithComputedVisibility(computed: boolean) { if (computed) { - this.filtersWithComputedVisibility += 1; + this.filters.pipe( + // Get filter data and check if we need to increment the counter + map(filtersData => { + if (filtersData && filtersData.hasSucceeded && filtersData.payload) { + const totalFilters = filtersData.payload.length; + const currentComputed = this.getCurrentFiltersComputed(this.currentConfiguration); + + // If we've already computed all filters for this configuration + if (currentComputed >= totalFilters) { + // Register in finalFiltersComputed if not already registered + if (!this.findConfigInFinalFilters(this.currentConfiguration)) { + this.updateFinalFiltersComputed(this.currentConfiguration, totalFilters); + } + return { shouldIncrement: false }; + } + + // We haven't reached the total yet, proceed with increment + return { + shouldIncrement: true, + totalFilters + }; + } + return { shouldIncrement: false }; + }), + // Only continue if we need to increment the counter + filter(result => result.shouldIncrement), + // Increment the counter for the current configuration + map(result => { + const filterConfig = this.findConfigInCurrentFilters(this.currentConfiguration); + + if (filterConfig) { + // Update existing counter + filterConfig.filtersComputed += 1; + } else { + // Create new counter entry + this.currentFiltersComputed.push({ + configuration: this.currentConfiguration, + filtersComputed: 1 + }); + } + + // Pass along the total and updated count + return { + totalFilters: result.totalFilters, + currentComputed: this.getCurrentFiltersComputed(this.currentConfiguration) + }; + }), + // Check if we've reached the total after incrementing + map(result => { + if (result.currentComputed === result.totalFilters) { + // If we've reached the total, update final filters count + this.updateFinalFiltersComputed(this.currentConfiguration, result.currentComputed); + } + return result; + }) + ).subscribe().unsubscribe(); // Execute the pipeline and immediately unsubscribe } } + + /** + * Finds a configuration entry in the currentFiltersComputed array + * @param configuration The configuration identifier to search for + * @returns The filter configuration object if found, otherwise undefined + */ + private findConfigInCurrentFilters(configuration: string) { + return this.currentFiltersComputed.find( + (configFilter) => configFilter.configuration === configuration + ); + } + + /** + * Finds a configuration entry in the finalFiltersComputed array + * @param configuration The configuration identifier to search for + * @returns The filter configuration object if found, otherwise undefined + */ + private findConfigInFinalFilters(configuration: string) { + return this.finalFiltersComputed.find( + (configFilter) => configFilter.configuration === configuration + ); + } + + /** + * Updates or adds a new entry in the finalFiltersComputed array + * @param configuration The configuration identifier to update + * @param count The number of computed filters to set for this configuration + */ + private updateFinalFiltersComputed(configuration: string, count: number) { + const filterConfig = this.findConfigInFinalFilters(configuration); + + if (filterConfig) { + filterConfig.filtersComputed = count; + } else { + this.finalFiltersComputed.push({ + configuration, + filtersComputed: count + }); + } + } + + /** + * Gets the current number of computed filters for a specific configuration + * @param configuration The configuration identifier to get the count for + * @returns The number of computed filters, or 0 if none found + */ + private getCurrentFiltersComputed(configuration: string) { + const configFilter = this.findConfigInCurrentFilters(configuration); + return configFilter?.filtersComputed || 0; + } + + /** + * Gets the final number of computed filters for a specific configuration + * @param configuration The configuration identifier to get the count for + * @returns The number of computed filters in the final state, or 0 if none found + */ + getFinalFiltersComputed(configuration: string): number { + const configFilter = this.findConfigInFinalFilters(configuration); + return configFilter?.filtersComputed || 0; + } } From ff5f23017a5d003a3613c88d74c5d8e8f26485b5 Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 26 Mar 2025 17:59:31 +0100 Subject: [PATCH 028/395] 129694: PoC #4099 solution with resolvers --- .../collection-page.component.html | 1 - .../community-page.component.html | 1 - src/app/home-page/home-page.component.html | 3 - .../full/full-item-page.component.html | 1 - .../item-page/simple/item-page.component.html | 1 - .../dspace/view-tracker-resolver.service.ts | 66 +++++++++++++++++++ .../dspace/view-tracker.component.html | 1 - .../dspace/view-tracker.component.scss | 3 - .../dspace/view-tracker.component.ts | 56 ---------------- .../dspace/view-tracker.resolver.ts | 11 ++++ 10 files changed, 77 insertions(+), 67 deletions(-) create mode 100644 src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts delete mode 100644 src/app/statistics/angulartics/dspace/view-tracker.component.html delete mode 100644 src/app/statistics/angulartics/dspace/view-tracker.component.scss delete mode 100644 src/app/statistics/angulartics/dspace/view-tracker.component.ts create mode 100644 src/app/statistics/angulartics/dspace/view-tracker.resolver.ts diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 02c63d316d8..1b9b810ea0d 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -3,7 +3,6 @@ *ngVar="(collectionRD$ | async) as collectionRD">
-
diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index 6d5262d9338..f36a59b23cd 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -1,7 +1,6 @@
-
diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html index caa86ac290a..e217e41a378 100644 --- a/src/app/home-page/home-page.component.html +++ b/src/app/home-page/home-page.component.html @@ -1,8 +1,5 @@
- - - diff --git a/src/app/item-page/full/full-item-page.component.html b/src/app/item-page/full/full-item-page.component.html index 1d831813956..39f682113a0 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -3,7 +3,6 @@
-
diff --git a/src/app/item-page/simple/item-page.component.html b/src/app/item-page/simple/item-page.component.html index cc9983bb354..5e2dc63ee9d 100644 --- a/src/app/item-page/simple/item-page.component.html +++ b/src/app/item-page/simple/item-page.component.html @@ -3,7 +3,6 @@
-
diff --git a/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts b/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts new file mode 100644 index 00000000000..f9a24e091ae --- /dev/null +++ b/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts @@ -0,0 +1,66 @@ +import { + AfterViewChecked, AfterViewInit, + Component, Injectable, + Input, + OnDestroy, + OnInit, +} from '@angular/core'; +import { Angulartics2 } from 'angulartics2'; +import { Observable, Subscription, switchMap } from 'rxjs'; +import { filter, take } from 'rxjs/operators'; + +import { ReferrerService } from '../../../core/services/referrer.service'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { hasValue } from '../../../shared/empty.util'; +import { ActivatedRoute, ActivatedRouteSnapshot, Resolve, ResolveEnd, Router, RouterStateSnapshot } from '@angular/router'; +import { BreadcrumbConfig } from '../../../breadcrumbs/breadcrumb/breadcrumb-config.model'; +import { SubmissionObject } from '../../../core/submission/models/submission-object.model'; + +/** + * This component triggers a page view statistic + */ +@Injectable({ + providedIn: 'root' +}) +export class ViewTrackerResolverService { + + constructor( + public angulartics2: Angulartics2, + public referrerService: ReferrerService, + public router: Router, + ) { + } + + resolve(routeSnapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + const dsoPath = routeSnapshot.data['dsoPath'] || 'dso.payload'; // Fetch the resolvers passed via the route data + this.router.events.pipe( + filter(event => event instanceof ResolveEnd), + take(1), + switchMap(() => + this.referrerService.getReferrer().pipe(take(1)))) + .subscribe((referrer: string) => { + this.angulartics2.eventTrack.next({ + action: 'page_view', + properties: { + object: this.getNestedProperty(routeSnapshot.data, dsoPath), + referrer, + }, + }); + }); + return true; + } + + private getNestedProperty(obj: any, path: string) { + const keys = path.split('.'); + let result = obj; + + for (const key of keys) { + if (result && result.hasOwnProperty(key)) { + result = result[key]; + } else { + return undefined; + } + } + return result; + } +} diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.html b/src/app/statistics/angulartics/dspace/view-tracker.component.html deleted file mode 100644 index c0c0ffe1810..00000000000 --- a/src/app/statistics/angulartics/dspace/view-tracker.component.html +++ /dev/null @@ -1 +0,0 @@ -  diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.scss b/src/app/statistics/angulartics/dspace/view-tracker.component.scss deleted file mode 100644 index c76cafbe449..00000000000 --- a/src/app/statistics/angulartics/dspace/view-tracker.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -:host { - display: none -} diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.ts b/src/app/statistics/angulartics/dspace/view-tracker.component.ts deleted file mode 100644 index 805d311cfd9..00000000000 --- a/src/app/statistics/angulartics/dspace/view-tracker.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Component, Input, OnInit, OnDestroy } from '@angular/core'; -import { Angulartics2 } from 'angulartics2'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { Subscription } from 'rxjs/internal/Subscription'; -import { take } from 'rxjs/operators'; -import { hasValue } from '../../../shared/empty.util'; -import { ReferrerService } from '../../../core/services/referrer.service'; - -/** - * This component triggers a page view statistic - */ -@Component({ - selector: 'ds-view-tracker', - styleUrls: ['./view-tracker.component.scss'], - templateUrl: './view-tracker.component.html', -}) -export class ViewTrackerComponent implements OnInit, OnDestroy { - /** - * The DSpaceObject to track a view event about - */ - @Input() object: DSpaceObject; - - /** - * The subscription on this.referrerService.getReferrer() - * @protected - */ - protected sub: Subscription; - - constructor( - public angulartics2: Angulartics2, - public referrerService: ReferrerService - ) { - } - - ngOnInit(): void { - this.sub = this.referrerService.getReferrer() - .pipe(take(1)) - .subscribe((referrer: string) => { - this.angulartics2.eventTrack.next({ - action: 'page_view', - properties: { - object: this.object, - referrer - }, - }); - }); - } - - ngOnDestroy(): void { - // unsubscribe in the case that this component is destroyed before - // this.referrerService.getReferrer() has emitted - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } - } -} diff --git a/src/app/statistics/angulartics/dspace/view-tracker.resolver.ts b/src/app/statistics/angulartics/dspace/view-tracker.resolver.ts new file mode 100644 index 00000000000..9b98185d618 --- /dev/null +++ b/src/app/statistics/angulartics/dspace/view-tracker.resolver.ts @@ -0,0 +1,11 @@ +import { inject } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot, } from '@angular/router'; +import { ViewTrackerResolverService } from './view-tracker-resolver.service'; + +export const viewTrackerResolver: ResolveFn = ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot, + viewTrackerResolverService: ViewTrackerResolverService = inject(ViewTrackerResolverService), +): boolean => { + return viewTrackerResolverService.resolve(route, state); +}; From 01becae7d0a99ea2d99d1e28d6e75c5f7f64b882 Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 4 Apr 2025 13:31:49 +0200 Subject: [PATCH 029/395] 129694: backported view tracker resolver --- src/app/app-routing.module.ts | 13 ++++++++++++- .../collection-page-routing.module.ts | 5 +++++ .../community-page/community-page-routing.module.ts | 5 +++++ src/app/home-page/home-page-routing.module.ts | 6 ------ src/app/item-page/item-page-routing.module.ts | 10 +++++++++- .../angulartics/dspace/view-tracker.resolver.ts | 11 ----------- src/app/statistics/statistics.module.ts | 3 --- 7 files changed, 31 insertions(+), 22 deletions(-) delete mode 100644 src/app/statistics/angulartics/dspace/view-tracker.resolver.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index deb68f1ea92..b81201dd01a 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -40,6 +40,8 @@ import { import { ServerCheckGuard } from './core/server-check/server-check.guard'; import { MenuResolver } from './menu.resolver'; import { ThemedPageErrorComponent } from './page-error/themed-page-error.component'; +import { HomePageResolver } from './home-page/home-page.resolver'; +import { ViewTrackerResolverService } from './statistics/angulartics/dspace/view-tracker-resolver.service'; @NgModule({ imports: [ @@ -63,7 +65,15 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone path: 'home', loadChildren: () => import('./home-page/home-page.module') .then((m) => m.HomePageModule), - data: { showBreadcrumbs: false }, + data: { + showBreadcrumbs: false, + dsoPath: 'site' + }, + resolve: { + site: HomePageResolver, + tracking: ViewTrackerResolverService, + }, + canActivate: [EndUserAgreementCurrentUserGuard] }, { @@ -251,6 +261,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone }) ], exports: [RouterModule], + providers: [HomePageResolver, ViewTrackerResolverService], }) export class AppRoutingModule { diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 9dc25b778ee..23a267bd03f 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -22,6 +22,7 @@ import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCollectionPageComponent } from './themed-collection-page.component'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { ViewTrackerResolverService } from '../statistics/angulartics/dspace/view-tracker-resolver.service'; @NgModule({ imports: [ @@ -66,6 +67,9 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; path: '', component: ThemedCollectionPageComponent, pathMatch: 'full', + resolve: { + tracking: ViewTrackerResolverService, + }, } ], data: { @@ -94,6 +98,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; LinkService, CreateCollectionPageGuard, CollectionPageAdministratorGuard, + ViewTrackerResolverService, ] }) export class CollectionPageRoutingModule { diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index c37f8832f84..48206d7dfcf 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -15,6 +15,7 @@ import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { ViewTrackerResolverService } from '../statistics/angulartics/dspace/view-tracker-resolver.service'; @NgModule({ imports: [ @@ -49,6 +50,9 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; path: '', component: ThemedCommunityPageComponent, pathMatch: 'full', + resolve: { + tracking: ViewTrackerResolverService, + }, } ], data: { @@ -76,6 +80,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; LinkService, CreateCommunityPageGuard, CommunityPageAdministratorGuard, + ViewTrackerResolverService, ] }) export class CommunityPageRoutingModule { diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index 196a290552a..aaac25c455f 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -28,15 +28,9 @@ import { MenuItemType } from '../shared/menu/menu-item-type.model'; } as LinkMenuItemModel, }], }, - }, - resolve: { - site: HomePageResolver } } ]) - ], - providers: [ - HomePageResolver ] }) export class HomePageRoutingModule { diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 0c855ab34dd..084c314d826 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -19,6 +19,7 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; import { OrcidPageComponent } from './orcid-page/orcid-page.component'; import { OrcidPageGuard } from './orcid-page/orcid-page.guard'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { ViewTrackerResolverService } from '../statistics/angulartics/dspace/view-tracker-resolver.service'; @NgModule({ imports: [ @@ -36,10 +37,16 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; path: '', component: ThemedItemPageComponent, pathMatch: 'full', + resolve: { + tracking: ViewTrackerResolverService, + } }, { path: 'full', component: ThemedFullItemPageComponent, + resolve: { + tracking: ViewTrackerResolverService, + } }, { path: ITEM_EDIT_PATH, @@ -98,7 +105,8 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; LinkService, ItemPageAdministratorGuard, VersionResolver, - OrcidPageGuard + OrcidPageGuard, + ViewTrackerResolverService, ] }) diff --git a/src/app/statistics/angulartics/dspace/view-tracker.resolver.ts b/src/app/statistics/angulartics/dspace/view-tracker.resolver.ts deleted file mode 100644 index 9b98185d618..00000000000 --- a/src/app/statistics/angulartics/dspace/view-tracker.resolver.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { inject } from '@angular/core'; -import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot, } from '@angular/router'; -import { ViewTrackerResolverService } from './view-tracker-resolver.service'; - -export const viewTrackerResolver: ResolveFn = ( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot, - viewTrackerResolverService: ViewTrackerResolverService = inject(ViewTrackerResolverService), -): boolean => { - return viewTrackerResolverService.resolve(route, state); -}; diff --git a/src/app/statistics/statistics.module.ts b/src/app/statistics/statistics.module.ts index 4870e4fbf0b..d93d7743948 100644 --- a/src/app/statistics/statistics.module.ts +++ b/src/app/statistics/statistics.module.ts @@ -2,7 +2,6 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CoreModule } from '../core/core.module'; import { SharedModule } from '../shared/shared.module'; -import { ViewTrackerComponent } from './angulartics/dspace/view-tracker.component'; import { StatisticsEndpoint } from './statistics-endpoint.model'; /** @@ -19,10 +18,8 @@ export const models = [ SharedModule, ], declarations: [ - ViewTrackerComponent, ], exports: [ - ViewTrackerComponent, ] }) /** From edd5496a4d946c8391e7171af2969a2a7cfc7c0e Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 4 Apr 2025 15:11:02 +0200 Subject: [PATCH 030/395] 129694: Fixed lint issues --- src/app/home-page/home-page-routing.module.ts | 1 - .../dspace/view-tracker-resolver.service.ts | 18 ++++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index aaac25c455f..b95a258613f 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -1,7 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { HomePageResolver } from './home-page.resolver'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedHomePageComponent } from './themed-home-page.component'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; diff --git a/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts b/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts index f9a24e091ae..fc23c09c51f 100644 --- a/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts +++ b/src/app/statistics/angulartics/dspace/view-tracker-resolver.service.ts @@ -1,20 +1,10 @@ -import { - AfterViewChecked, AfterViewInit, - Component, Injectable, - Input, - OnDestroy, - OnInit, -} from '@angular/core'; +import { Injectable, } from '@angular/core'; import { Angulartics2 } from 'angulartics2'; -import { Observable, Subscription, switchMap } from 'rxjs'; +import { switchMap } from 'rxjs'; import { filter, take } from 'rxjs/operators'; import { ReferrerService } from '../../../core/services/referrer.service'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { hasValue } from '../../../shared/empty.util'; -import { ActivatedRoute, ActivatedRouteSnapshot, Resolve, ResolveEnd, Router, RouterStateSnapshot } from '@angular/router'; -import { BreadcrumbConfig } from '../../../breadcrumbs/breadcrumb/breadcrumb-config.model'; -import { SubmissionObject } from '../../../core/submission/models/submission-object.model'; +import { ActivatedRouteSnapshot, ResolveEnd, Router, RouterStateSnapshot } from '@angular/router'; /** * This component triggers a page view statistic @@ -32,7 +22,7 @@ export class ViewTrackerResolverService { } resolve(routeSnapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { - const dsoPath = routeSnapshot.data['dsoPath'] || 'dso.payload'; // Fetch the resolvers passed via the route data + const dsoPath = routeSnapshot.data.dsoPath || 'dso.payload'; // Fetch the resolvers passed via the route data this.router.events.pipe( filter(event => event instanceof ResolveEnd), take(1), From 0d2e49b12c744b6fe6bf82e4a9e698c1a21d6b9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 03:06:35 +0000 Subject: [PATCH 031/395] Bump sass from 1.84.0 to 1.85.1 in the sass group across 1 directory Bumps the sass group with 1 update in the / directory: [sass](https://github.com/sass/dart-sass). Updates `sass` from 1.84.0 to 1.85.1 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.84.0...1.85.1) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-minor dependency-group: sass ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1ab4e54262f..7edbf9ea198 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "react-copy-to-clipboard": "^5.1.0", "react-dom": "^16.14.0", "rimraf": "^3.0.2", - "sass": "~1.84.0", + "sass": "~1.86.3", "sass-loader": "^12.6.0", "sass-resources-loader": "^2.2.5", "ts-node": "^8.10.2", diff --git a/yarn.lock b/yarn.lock index 7983caea239..e6abd594b70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10270,10 +10270,10 @@ sass@1.58.1: immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" -sass@^1.25.0, sass@~1.84.0: - version "1.84.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.84.0.tgz#da9154cbccb2d2eac7a9486091b6d9ba93ef5bad" - integrity sha512-XDAbhEPJRxi7H0SxrnOpiXFQoUJHwkR2u3Zc4el+fK/Tt5Hpzw5kkQ59qVDfvdaUq6gCrEZIbySFBM2T9DNKHg== +sass@^1.25.0, sass@~1.86.3: + version "1.86.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.86.3.tgz#0a0d9ea97cb6665e73f409639f8533ce057464c9" + integrity sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw== dependencies: chokidar "^4.0.0" immutable "^5.0.2" From 33146603dffd00ff0690d078034e5e69950e1d74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 22:26:28 +0000 Subject: [PATCH 032/395] Bump eslint-plugin-jsonc from 2.19.1 to 2.20.0 in the eslint group Bumps the eslint group with 1 update: [eslint-plugin-jsonc](https://github.com/ota-meshi/eslint-plugin-jsonc). Updates `eslint-plugin-jsonc` from 2.19.1 to 2.20.0 - [Release notes](https://github.com/ota-meshi/eslint-plugin-jsonc/releases) - [Changelog](https://github.com/ota-meshi/eslint-plugin-jsonc/blob/master/CHANGELOG.md) - [Commits](https://github.com/ota-meshi/eslint-plugin-jsonc/compare/v2.19.1...v2.20.0) --- updated-dependencies: - dependency-name: eslint-plugin-jsonc dependency-version: 2.20.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 88 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 30cbf522ade..50f27bf8c57 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,7 @@ "eslint-plugin-deprecation": "^1.5.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsdoc": "^45.0.0", - "eslint-plugin-jsonc": "^2.19.1", + "eslint-plugin-jsonc": "^2.20.0", "eslint-plugin-lodash": "^7.4.0", "eslint-plugin-unused-imports": "^2.0.0", "express-static-gzip": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index 73194d1c4f1..caea3d149ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1712,12 +1712,12 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.8.tgz#200a0965cf654ac28b971358ecdca9cc5b44c335" integrity sha512-1iuezdyDNngPnz8rLRDO2C/ZZ/emJLb72OsZeqQ6gL6Avko/XCXZw+NuxBSNhBAP13Hie418V7VMt9et1FMvpg== -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.5.1": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz#b0fc7e06d0c94f801537fd4237edc2706d3b8e4c" + integrity sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w== dependencies: - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.4.0": version "4.5.0" @@ -2243,6 +2243,11 @@ "@parcel/watcher-win32-ia32" "2.4.1" "@parcel/watcher-win32-x64" "2.4.1" +"@pkgr/core@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.2.tgz#224e466468489466a3ca77832be80cdd0bb8e523" + integrity sha512-25L86MyPvnlQoX2MTIV2OiUcb6vJ6aRbFa9pbwByn95INKD5mFH2smgjDhq+fwJoqAgvgbdJLj6Tz7V9X5CFAQ== + "@react-dnd/asap@^4.0.0": version "4.0.1" resolved "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz" @@ -3037,6 +3042,11 @@ acorn@^8.1.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.13.0.tgz#2a30d670818ad16ddd6a35d3842dacec9e5d7ca3" integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w== +acorn@^8.14.0: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + adjust-sourcemap-loader@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz" @@ -5299,10 +5309,10 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-compat-utils@^0.6.0: - version "0.6.4" - resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.6.4.tgz#173d305132da755ac3612cccab03e1b2e14235ed" - integrity sha512-/u+GQt8NMfXO8w17QendT4gvO5acfxQsAKirAt0LVxDnr2N8YLCVbregaNc/Yhp7NM128DwCaRvr8PLDfeNkQw== +eslint-compat-utils@^0.6.4: + version "0.6.5" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz#6b06350a1c947c4514cfa64a170a6bfdbadc7ec2" + integrity sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ== dependencies: semver "^7.5.4" @@ -5377,19 +5387,19 @@ eslint-plugin-jsdoc@^45.0.0: semver "^7.5.1" spdx-expression-parse "^3.0.1" -eslint-plugin-jsonc@^2.19.1: - version "2.19.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.19.1.tgz#aeedd7131d115b8c46f439a8855139837a0e2752" - integrity sha512-MmlAOaZK1+Lg7YoCZPGRjb88ZjT+ct/KTsvcsbZdBm+w8WMzGx+XEmexk0m40P1WV9G2rFV7X3klyRGRpFXEjA== +eslint-plugin-jsonc@^2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.20.0.tgz#7f3ae51abd38176487ba7324dee77578a92e15e0" + integrity sha512-FRgCn9Hzk5eKboCbVMrr9QrhM0eO4G+WKH8IFXoaeqhM/2kuWzbStJn4kkr0VWL8J5H8RYZF+Aoam1vlBaZVkw== dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - eslint-compat-utils "^0.6.0" + "@eslint-community/eslint-utils" "^4.5.1" + eslint-compat-utils "^0.6.4" eslint-json-compat-utils "^0.2.1" - espree "^9.6.1" + espree "^9.6.1 || ^10.3.0" graphemer "^1.4.0" - jsonc-eslint-parser "^2.0.4" + jsonc-eslint-parser "^2.4.0" natural-compare "^1.4.0" - synckit "^0.6.0" + synckit "^0.6.2 || ^0.7.3 || ^0.10.3" eslint-plugin-lodash@^7.4.0: version "7.4.0" @@ -5451,11 +5461,16 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz" integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== -eslint-visitor-keys@^3.4.1: +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + eslint@^8.39.0: version "8.39.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" @@ -5507,7 +5522,7 @@ esm@^3.2.25: resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^9.0.0, espree@^9.5.1, espree@^9.6.1: +espree@^9.0.0, espree@^9.5.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -5516,6 +5531,15 @@ espree@^9.0.0, espree@^9.5.1, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +"espree@^9.6.1 || ^10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" @@ -7350,10 +7374,10 @@ json5@^2.1.2, json5@^2.2.1, json5@^2.2.2, json5@^2.2.3: resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-eslint-parser@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz#4c126b530aa583d85308d0b3041ff81ce402bbb2" - integrity sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g== +jsonc-eslint-parser@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz#74ded53f9d716e8d0671bd167bf5391f452d5461" + integrity sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg== dependencies: acorn "^8.5.0" eslint-visitor-keys "^3.0.0" @@ -10997,12 +11021,13 @@ symbol-tree@^3.2.4: resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -synckit@^0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.6.2.tgz#e1540b97825f2855f7170b98276e8463167f33eb" - integrity sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA== +"synckit@^0.6.2 || ^0.7.3 || ^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.10.3.tgz#940aea2c7b6d141a4f74dbdebc81e0958c331a4b" + integrity sha512-R1urvuyiTaWfeCggqEvpDJwAlDVdsT9NM+IP//Tk2x7qHCkSvBk/fwFgw/TLAHzZlrAnnazMcRw0ZD8HlYFTEQ== dependencies: - tslib "^2.3.1" + "@pkgr/core" "^0.2.0" + tslib "^2.8.1" tapable@^2.1.1, tapable@^2.2.0: version "2.2.1" @@ -11260,6 +11285,11 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" From b37a7a1456b0230639a67a26727e0e83f1740ab8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 22:27:19 +0000 Subject: [PATCH 033/395] Bump isbot from 5.1.25 to 5.1.26 Bumps [isbot](https://github.com/omrilotan/isbot) from 5.1.25 to 5.1.26. - [Changelog](https://github.com/omrilotan/isbot/blob/main/CHANGELOG.md) - [Commits](https://github.com/omrilotan/isbot/compare/v5.1.25...v5.1.26) --- updated-dependencies: - dependency-name: isbot dependency-version: 5.1.26 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 30cbf522ade..c4fc2a42844 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "filesize": "^6.1.0", "http-proxy-middleware": "^2.0.7", "http-terminator": "^3.2.0", - "isbot": "^5.1.25", + "isbot": "^5.1.26", "js-cookie": "2.2.1", "js-yaml": "^4.1.0", "json5": "^2.2.3", diff --git a/yarn.lock b/yarn.lock index 73194d1c4f1..cee2d31bcef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7080,10 +7080,10 @@ isbinaryfile@^4.0.8: resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz" integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== -isbot@^5.1.25: - version "5.1.25" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.25.tgz#3f08f13a773a9cd86f7e498094f36974de0c151d" - integrity sha512-mqU76fmT7cpGG0JX1EzhCZIC+xovpH6TD2SAK18alonk0RG/RgChpGduJTYzRaq9a0COoFA99M9JVtEUOcScIw== +isbot@^5.1.26: + version "5.1.26" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.26.tgz#38c336503b8a7071a15437b2d11b41e65b903bd2" + integrity sha512-3wqJEYSIm59dYQjEF7zJ7T42aqaqxbCyJQda5rKCudJykuAnISptCHR/GSGpOnw8UrvU+mGueNLRJS5HXnbsXQ== isexe@^2.0.0: version "2.0.0" From 4f42c1e95f9859f8997e2a290263822237b41f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Thu, 10 Apr 2025 17:34:36 +0200 Subject: [PATCH 034/395] Use take instead unsubscribe --- .../shared/search/search-filters/search-filters.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index b23a6452055..b5aa10778d6 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -2,7 +2,7 @@ import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; -import { map, filter } from 'rxjs/operators'; +import { map, filter, take } from 'rxjs/operators'; import { SearchService } from '../../../core/shared/search/search.service'; import { RemoteData } from '../../../core/data/remote-data'; @@ -188,7 +188,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { } return result; }) - ).subscribe().unsubscribe(); // Execute the pipeline and immediately unsubscribe + ).pipe(take(1)).subscribe(); // Execute the pipeline and immediately unsubscribe } } From 036fd1c87191b3116e10dc62a11488444b731269 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 19:27:52 +0000 Subject: [PATCH 035/395] Bump the testing group with 2 updates Bumps the testing group with 2 updates: [axe-core](https://github.com/dequelabs/axe-core) and [ng-mocks](https://github.com/help-me-mom/ng-mocks). Updates `axe-core` from 4.10.2 to 4.10.3 - [Release notes](https://github.com/dequelabs/axe-core/releases) - [Changelog](https://github.com/dequelabs/axe-core/blob/v4.10.3/CHANGELOG.md) - [Commits](https://github.com/dequelabs/axe-core/compare/v4.10.2...v4.10.3) Updates `ng-mocks` from 14.13.2 to 14.13.3 - [Release notes](https://github.com/help-me-mom/ng-mocks/releases) - [Changelog](https://github.com/help-me-mom/ng-mocks/blob/master/CHANGELOG.md) - [Commits](https://github.com/help-me-mom/ng-mocks/compare/v14.13.2...v14.13.3) --- updated-dependencies: - dependency-name: axe-core dependency-type: direct:development update-type: version-update:semver-patch dependency-group: testing - dependency-name: ng-mocks dependency-type: direct:development update-type: version-update:semver-patch dependency-group: testing ... Signed-off-by: dependabot[bot] --- package.json | 4 ++-- yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 50f27bf8c57..dc23b0a2734 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "@types/sanitize-html": "^2.13.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", - "axe-core": "^4.10.2", + "axe-core": "^4.10.3", "compression-webpack-plugin": "^9.2.0", "copy-webpack-plugin": "^6.4.1", "cross-env": "^7.0.3", @@ -175,7 +175,7 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", - "ng-mocks": "^14.13.2", + "ng-mocks": "^14.13.4", "ngx-mask": "^13.1.7", "nodemon": "^2.0.22", "postcss": "^8.5", diff --git a/yarn.lock b/yarn.lock index caea3d149ce..fae90b27b9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3414,10 +3414,10 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== -axe-core@^4.10.2: - version "4.10.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" - integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== +axe-core@^4.10.3: + version "4.10.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.3.tgz#04145965ac7894faddbac30861e5d8f11bfd14fc" + integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg== axios@0.21.4: version "0.21.4" @@ -8399,10 +8399,10 @@ neo-async@^2.6.2: resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -ng-mocks@^14.13.2: - version "14.13.2" - resolved "https://registry.yarnpkg.com/ng-mocks/-/ng-mocks-14.13.2.tgz#ddd675d675eb16dfa85834e28dd42343853a6622" - integrity sha512-ItAB72Pc0uznL1j4TPsFp1wehhitVp7DARkc67aafeIk1FDgwnAZvzJwntMnIp/IWMSbzrEQ6kl3cc5euX1NRA== +ng-mocks@^14.13.4: + version "14.13.4" + resolved "https://registry.yarnpkg.com/ng-mocks/-/ng-mocks-14.13.4.tgz#bb53f7b75e0937fcfc8a2177a234a0937143f326" + integrity sha512-OFpzcx9vzeMqpVaBaukH8gvHRhor8iAkc8pOWakSPwxD3DuoHyDrjb/odgjI3Jq+Iaerqb3js1I4Sluu+0rLSQ== ng2-file-upload@1.4.0: version "1.4.0" From 828648aa7e6c9e3a433b4376355c265b67a8d944 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:56:43 +0000 Subject: [PATCH 036/395] Bump axios from 1.7.9 to 1.8.4 Bumps [axios](https://github.com/axios/axios) from 1.7.9 to 1.8.4. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.9...v1.8.4) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index dc23b0a2734..c095aff2c91 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@nicky-lenaers/ngx-scroll-to": "^14.0.0", "angular-idle-preload": "3.0.0", "angulartics2": "^12.2.1", - "axios": "^1.7.9", + "axios": "^1.8.4", "bootstrap": "^4.6.1", "cerialize": "0.1.18", "cli-progress": "^3.12.0", diff --git a/yarn.lock b/yarn.lock index fae90b27b9b..2e18ecafd95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3426,10 +3426,10 @@ axios@0.21.4: dependencies: follow-redirects "^1.14.0" -axios@^1.7.9: - version "1.7.9" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a" - integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw== +axios@^1.8.4: + version "1.8.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.4.tgz#78990bb4bc63d2cae072952d374835950a82f447" + integrity sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" From dc3e84274759bb29b5165dbad4eea5828d65705e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Fern=C3=A1ndez=20Celorio?= Date: Fri, 11 Apr 2025 11:39:41 +0200 Subject: [PATCH 037/395] No need to subscribe anymore to the router events --- .../search-filters/search-filters.component.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index b5aa10778d6..189062fea34 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -95,15 +95,13 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.router.events.subscribe(() => { - this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe( - map((filters) => { - Object.keys(filters).forEach((f) => filters[f] = null); - return filters; - }) - ); - this.searchLink = this.getSearchLink(); - }); + this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe( + map((filters) => { + Object.keys(filters).forEach((f) => filters[f] = null); + return filters; + }) + ); + this.searchLink = this.getSearchLink(); } /** From cc202b7adb3a632a3cbc63669c1d4501cce2512b Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 9 Apr 2025 12:03:11 +0200 Subject: [PATCH 038/395] 130081: Fix pagination on 'Select bistreams' modal on 'Access Control' tab (cherry picked from commit c03bbb01a2d4ebdedacff3b046a7ce0522bf971f) --- ...rol-select-bitstreams-modal.component.html | 13 +++-- ...ntrol-select-bitstreams-modal.component.ts | 47 +++++++++---------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/app/shared/access-control-form-container/item-access-control-select-bitstreams-modal/item-access-control-select-bitstreams-modal.component.html b/src/app/shared/access-control-form-container/item-access-control-select-bitstreams-modal/item-access-control-select-bitstreams-modal.component.html index 8cf0ecea382..88706e2df3c 100644 --- a/src/app/shared/access-control-form-container/item-access-control-select-bitstreams-modal/item-access-control-select-bitstreams-modal.component.html +++ b/src/app/shared/access-control-form-container/item-access-control-select-bitstreams-modal/item-access-control-select-bitstreams-modal.component.html @@ -8,20 +8,19 @@
diff --git a/src/app/item-page/alerts/item-alerts.component.html b/src/app/item-page/alerts/item-alerts.component.html index cd71d23a910..b9b30f283be 100644 --- a/src/app/item-page/alerts/item-alerts.component.html +++ b/src/app/item-page/alerts/item-alerts.component.html @@ -6,7 +6,7 @@
diff --git a/src/app/item-page/field-components/collections/collections.component.html b/src/app/item-page/field-components/collections/collections.component.html index 27ebb41d611..dfa22237ae7 100644 --- a/src/app/item-page/field-components/collections/collections.component.html +++ b/src/app/item-page/field-components/collections/collections.component.html @@ -1,6 +1,6 @@ @@ -15,6 +15,7 @@ class="load-more-btn btn btn-sm btn-outline-secondary" role="button" href="javascript:void(0);" + tabindex="0" > {{'item.page.collections.load-more' | translate}} diff --git a/src/app/item-page/field-components/metadata-uri-values/metadata-uri-values.component.html b/src/app/item-page/field-components/metadata-uri-values/metadata-uri-values.component.html index 7d7174536b1..5e575e63b5f 100644 --- a/src/app/item-page/field-components/metadata-uri-values/metadata-uri-values.component.html +++ b/src/app/item-page/field-components/metadata-uri-values/metadata-uri-values.component.html @@ -1,5 +1,5 @@ - + {{ linktext || mdValue.value }} diff --git a/src/app/item-page/field-components/metadata-values/metadata-values.component.html b/src/app/item-page/field-components/metadata-values/metadata-values.component.html index a9576da26a3..de6f4139941 100644 --- a/src/app/item-page/field-components/metadata-values/metadata-values.component.html +++ b/src/app/item-page/field-components/metadata-values/metadata-values.component.html @@ -21,7 +21,9 @@ @@ -35,5 +37,5 @@ {{value}} + [queryParams]="getQueryParams(value)" role="link" tabindex="0">{{value}} diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index 3749f639644..c886f5512df 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -85,7 +85,7 @@ diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index 904b7e039cc..e446fffbdba 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -71,7 +71,7 @@ diff --git a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.html b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.html index e1cf58b5b2f..8b3e2ebded5 100644 --- a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.html +++ b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.html @@ -3,6 +3,6 @@

{{"error.identifier" | translate}}

{{missingItem}}


- {{"404.link.home-page" | translate}} + {{"404.link.home-page" | translate}}

diff --git a/src/app/page-error/page-error.component.html b/src/app/page-error/page-error.component.html index 9a5f02600ae..9a8ffc7e90a 100644 --- a/src/app/page-error/page-error.component.html +++ b/src/app/page-error/page-error.component.html @@ -5,6 +5,6 @@

{{"error-page.description." + status | translate}}

{{"error-page." + code | translate}}


- {{ status + ".link.home-page" | translate}} + {{ status + ".link.home-page" | translate}}

diff --git a/src/app/pagenotfound/pagenotfound.component.html b/src/app/pagenotfound/pagenotfound.component.html index e85316b0ec1..dba2f9700e6 100644 --- a/src/app/pagenotfound/pagenotfound.component.html +++ b/src/app/pagenotfound/pagenotfound.component.html @@ -5,6 +5,6 @@

{{"404.page-not-found" | translate}}

{{"404.help" | translate}}


- {{"404.link.home-page" | translate}} + {{"404.link.home-page" | translate}}

\ No newline at end of file diff --git a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html index 4837e0dcda1..40c5548df7a 100644 --- a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html +++ b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html @@ -8,13 +8,15 @@

{{'grant-deny-request-copy.header' | translate}}

@@ -22,7 +24,7 @@

{{'grant-deny-request-copy.header' | translate}}

diff --git a/src/app/search-navbar/search-navbar.component.html b/src/app/search-navbar/search-navbar.component.html index e9b5f285fbc..84ffb5b38dc 100644 --- a/src/app/search-navbar/search-navbar.component.html +++ b/src/app/search-navbar/search-navbar.component.html @@ -7,7 +7,7 @@ [class.display]="searchExpanded ? 'inline-block' : 'none'" [tabIndex]="searchExpanded ? 0 : -1" [attr.data-test]="'header-search-box' | dsBrowserOnly"> - diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html index 07b37e220dd..05eaad43516 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html @@ -6,6 +6,7 @@
- + {{ 'nav.login' | translate }}(current) - + (current) diff --git a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html index c0ead4f46a2..c38b0391483 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html @@ -9,7 +9,8 @@

{{'browse.comcol.head' | translate}}

role="tab" [routerLink]="option.routerLink" [queryParams]="option.params" - [class.active]="(currentOptionId$ | async) === option.id">{{ option.label | translate }} + [class.active]="(currentOptionId$ | async) === option.id" + tabindex="0">{{ option.label | translate }}
diff --git a/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html index 01a71ab142b..42bbb9328ca 100644 --- a/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html +++ b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html @@ -1,4 +1,4 @@

{{ title | translate }} - {{getHandle()}} + {{getHandle()}}

diff --git a/src/app/shared/file-download-link/file-download-link.component.html b/src/app/shared/file-download-link/file-download-link.component.html index 59f255a652b..2a01d9dbdc5 100644 --- a/src/app/shared/file-download-link/file-download-link.component.html +++ b/src/app/shared/file-download-link/file-download-link.component.html @@ -1,8 +1,10 @@ - + [attr.aria-label]="('file-download-link.download' | translate) + dsoNameService.getName(bitstream)" + role="link" + tabindex="0"> diff --git a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html index ff265b40c8f..0f4b436cfb6 100644 --- a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html +++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html @@ -7,15 +7,15 @@ [placeholder]="'vocabulary-treeview.search.form.search-placeholder' | translate">
@@ -46,6 +46,8 @@

+ (click)="onSelect(node.item)" + role="button" + tabindex="0"> {{node.item.display}} @@ -64,7 +68,9 @@

@@ -78,6 +84,8 @@

+ (click)="onSelect(node.item)" + role="button" + tabindex="0"> {{node.item.display}} diff --git a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html index 378bb6ed67f..5c780d5c3d4 100644 --- a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html @@ -1,3 +1,3 @@ - diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.html b/src/app/shared/log-in/methods/password/log-in-password.component.html index 865247e39ab..28a44004702 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.html +++ b/src/app/shared/log-in/methods/password/log-in-password.component.html @@ -24,14 +24,14 @@ @fadeOut>{{ (message | async) | translate }}

+ [dsBtnDisabled]="!form.valid" role="button" tabindex="0"> {{"login.form.submit" | translate}} diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.html b/src/app/shared/menu/menu-item/link-menu-item.component.html index 71eeda2e68f..f96084e6e13 100644 --- a/src/app/shared/menu/menu-item/link-menu-item.component.html +++ b/src/app/shared/menu/menu-item/link-menu-item.component.html @@ -8,4 +8,5 @@ (keyup.space)="navigate($event)" (keydown.enter)="navigate($event)" href="javascript:void(0);" + tabindex="0" >{{item.text | translate}} diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.html b/src/app/shared/menu/menu-item/text-menu-item.component.html index ba3cf99a490..e2dd334caf5 100644 --- a/src/app/shared/menu/menu-item/text-menu-item.component.html +++ b/src/app/shared/menu/menu-item/text-menu-item.component.html @@ -1 +1 @@ -{{item.text | translate}} +{{item.text | translate}} diff --git a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html index ff7efcc309d..539ce77939a 100644 --- a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html +++ b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.html @@ -1,5 +1,5 @@
- + {{object.value}} diff --git a/src/app/shared/object-list/collection-list-element/collection-list-element.component.html b/src/app/shared/object-list/collection-list-element/collection-list-element.component.html index c50b382495c..f8c8a391252 100644 --- a/src/app/shared/object-list/collection-list-element/collection-list-element.component.html +++ b/src/app/shared/object-list/collection-list-element/collection-list-element.component.html @@ -1,5 +1,5 @@
- + {{ dsoNameService.getName(object) }} diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.html b/src/app/shared/object-list/community-list-element/community-list-element.component.html index 2101261bdc2..e00121c6666 100644 --- a/src/app/shared/object-list/community-list-element/community-list-element.component.html +++ b/src/app/shared/object-list/community-list-element/community-list-element.component.html @@ -1,5 +1,5 @@
- + {{ dsoNameService.getName(object) }} diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html index 7d416e9f3eb..e94427a6fd7 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html @@ -4,14 +4,16 @@ {{mdRepresentation.getValue()}} + target="_blank" [href]="mdRepresentation.getValue()" role="link" tabindex="0"> {{mdRepresentation.getValue()}} {{mdRepresentation.getValue()}} + [queryParams]="getQueryParams()" + role="link" + tabindex="0"> {{mdRepresentation.getValue()}}
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index f7a687048a2..f2ec34b4ef2 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -1,7 +1,7 @@
+ [routerLink]="[itemPageRoute]" class="dont-break-out" role="link" tabindex="0"> @@ -18,7 +18,7 @@ + [innerHTML]="dsoTitle" role="link" tabindex="0"> diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html index 836c94bf6b3..281a009d2e5 100644 --- a/src/app/shared/pagination/pagination.component.html +++ b/src/app/shared/pagination/pagination.component.html @@ -7,13 +7,13 @@
- +
diff --git a/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html index 91738c6e9af..4b0baed7d62 100644 --- a/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html @@ -8,11 +8,11 @@ diff --git a/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html index 7d0ad899142..1f7ef98c839 100644 --- a/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html @@ -8,11 +8,11 @@ diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html index 0ebd7ee6be9..2dc3c4c06f1 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html @@ -2,9 +2,9 @@ [tabIndex]="-1" [routerLink]="[searchLink]" [queryParams]="addQueryParams" queryParamsHandling="merge" - (click)="announceFilter()"> + (click)="announceFilter()" role="button" tabindex="0">
- @@ -44,7 +44,7 @@
-
diff --git a/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.html index 91738c6e9af..c1cdc3ea0cc 100644 --- a/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.html @@ -8,11 +8,11 @@ diff --git a/src/app/shared/search/search-results/search-results.component.html b/src/app/shared/search/search-results/search-results.component.html index a2bc91195ce..c396fce5fb7 100644 --- a/src/app/shared/search/search-results/search-results.component.html +++ b/src/app/shared/search/search-results/search-results.component.html @@ -32,7 +32,7 @@

{{ (configuration ? configuration + '.search.results. {{ 'search.results.no-results' | translate }} + queryParamsHandling="merge" role="link" tabindex="0"> {{"search.results.no-results-link" | translate}}

diff --git a/src/app/shared/starts-with/date/starts-with-date.component.html b/src/app/shared/starts-with/date/starts-with-date.component.html index b350d9896c6..c3fbb884ab5 100644 --- a/src/app/shared/starts-with/date/starts-with-date.component.html +++ b/src/app/shared/starts-with/date/starts-with-date.component.html @@ -27,7 +27,7 @@
- +
diff --git a/src/app/shared/starts-with/text/starts-with-text.component.html b/src/app/shared/starts-with/text/starts-with-text.component.html index edbd1f1960d..3952f86fda6 100644 --- a/src/app/shared/starts-with/text/starts-with-text.component.html +++ b/src/app/shared/starts-with/text/starts-with-text.component.html @@ -4,7 +4,7 @@
- +
diff --git a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html index 55ebe2d9575..1ac89e5329d 100644 --- a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html +++ b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html @@ -10,6 +10,7 @@ (keyup.Space)="toggle()" role="button" [attr.aria-expanded]="isExpanded" + tabindex="0" > {{ 'item.truncatable-part.show-' + (isExpanded ? 'less' : 'more') | translate }} diff --git a/src/themes/dspace/app/header/header.component.html b/src/themes/dspace/app/header/header.component.html index 02226ac0a1c..eaf107dff8d 100644 --- a/src/themes/dspace/app/header/header.component.html +++ b/src/themes/dspace/app/header/header.component.html @@ -5,7 +5,7 @@ [attr.role]="(isMobile$ | async) ? 'navigation' : 'presentation'" [attr.aria-label]="(isMobile$ | async) ? ('nav.main.description' | translate) : null" class="h-100 flex-fill d-flex flex-row flex-nowrap justify-content-start align-items-center gapx-3"> - +
From cd0aa134f8b610db100be233235bfdd06b51691f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Chaco=CC=81n?= Date: Fri, 9 May 2025 15:07:55 -0600 Subject: [PATCH 075/395] fixed some dynamic params over components --- .../models/relation-group/dynamic-relation-group.component.html | 2 +- .../search-facet-option/search-facet-option.component.html | 1 - .../search-facet-selected-option.component.html | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html index 6a0e34f6719..985d3187a66 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html @@ -62,7 +62,7 @@ diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html index 1bf9bb3bfef..7f53a9298da 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html @@ -1,5 +1,4 @@ + (click)="announceFilter()"> + [queryParams]="changeQueryParams" queryParamsHandling="merge"> {{filterValue.label}} {{filterValue.count | dsShortNumber}} diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html index 7f53a9298da..c6c64f8aa18 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html @@ -1,6 +1,7 @@ + [queryParams]="removeQueryParams" queryParamsHandling="merge">