Skip to content

Commit cf663a2

Browse files
UoE/Upgrade to 8.3.
UoE/Upgrade to 8.3.
2 parents 83efa3a + 5e44747 commit cf663a2

112 files changed

Lines changed: 3154 additions & 1913 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/codescan.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ jobs:
5050

5151
# Perform GitHub Code Scanning.
5252
- name: Perform CodeQL Analysis
53-
uses: github/codeql-action/analyze@v3
53+
uses: github/codeql-action/analyze@v3

cypress/e2e/item-edit.cy.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,27 @@ describe('Edit Item > Edit Metadata tab', () => {
2626
// Wait for any loading spinners to disappear
2727
cy.get('ds-edit-item-page ds-loading').should('not.exist');
2828

29+
// wait for all the tabs to be rendered on this page
30+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
31+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
32+
});
33+
2934
// wait for all the ds-dso-edit-metadata-value components to be rendered
3035
cy.get('ds-dso-edit-metadata-value div[role="row"]').each(($row: HTMLDivElement) => {
3136
cy.wrap($row).find('div[role="cell"]').should('be.visible');
3237
});
3338

3439
// Analyze <ds-edit-item-page> for accessibility issues
35-
testA11y('ds-edit-item-page');
40+
testA11y('ds-edit-item-page', {
41+
rules: {
42+
// Edit-metadata uses nested role="table" wrappers (per-field value lists) which
43+
// briefly contain no rows while values are hydrating, causing a flaky
44+
// "aria-required-children" violation. Same ng-bootstrap / nested-table
45+
// limitation as DSpace issue #2216 (see health-page.cy.ts which waives
46+
// this rule for the same reason).
47+
'aria-required-children': { enabled: false },
48+
},
49+
} as Options);
3650
});
3751
});
3852

@@ -49,6 +63,11 @@ describe('Edit Item > Status tab', () => {
4963
// <ds-item-status> tag must be loaded
5064
cy.get('ds-item-status').should('be.visible');
5165

66+
// wait for all the tabs to be rendered on this page
67+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
68+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
69+
});
70+
5271
// Analyze for accessibility issues
5372
testA11y('ds-item-status');
5473
});
@@ -67,6 +86,11 @@ describe('Edit Item > Bitstreams tab', () => {
6786
// <ds-item-bitstreams> tag must be loaded
6887
cy.get('ds-item-bitstreams').should('be.visible');
6988

89+
// wait for all the tabs to be rendered on this page
90+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
91+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
92+
});
93+
7094
// Table of item bitstreams must also be loaded
7195
cy.get('div.item-bitstreams').should('be.visible');
7296

@@ -96,6 +120,11 @@ describe('Edit Item > Curate tab', () => {
96120
// <ds-item-curate> tag must be loaded
97121
cy.get('ds-item-curate').should('be.visible');
98122

123+
// wait for all the tabs to be rendered on this page
124+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
125+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
126+
});
127+
99128
// Analyze for accessibility issues
100129
testA11y('ds-item-curate');
101130
});
@@ -114,6 +143,11 @@ describe('Edit Item > Relationships tab', () => {
114143
// <ds-item-relationships> tag must be loaded
115144
cy.get('ds-item-relationships').should('be.visible');
116145

146+
// wait for all the tabs to be rendered on this page
147+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
148+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
149+
});
150+
117151
// Analyze for accessibility issues
118152
testA11y('ds-item-relationships');
119153
});
@@ -132,6 +166,11 @@ describe('Edit Item > Version History tab', () => {
132166
// <ds-item-version-history> tag must be loaded
133167
cy.get('ds-item-version-history').should('be.visible');
134168

169+
// wait for all the tabs to be rendered on this page
170+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
171+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
172+
});
173+
135174
// Analyze for accessibility issues
136175
testA11y('ds-item-version-history');
137176
});
@@ -150,6 +189,11 @@ describe('Edit Item > Access Control tab', () => {
150189
// <ds-item-access-control> tag must be loaded
151190
cy.get('ds-item-access-control').should('be.visible');
152191

192+
// wait for all the tabs to be rendered on this page
193+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
194+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
195+
});
196+
153197
// Analyze for accessibility issues
154198
testA11y('ds-item-access-control');
155199
});
@@ -168,6 +212,11 @@ describe('Edit Item > Collection Mapper tab', () => {
168212
// <ds-item-collection-mapper> tag must be loaded
169213
cy.get('ds-item-collection-mapper').should('be.visible');
170214

215+
// wait for all the tabs to be rendered on this page
216+
cy.get('ds-edit-item-page ul[role="tablist"]').each(($row: HTMLUListElement) => {
217+
cy.wrap($row).find('a[role="tab"]').should('be.visible');
218+
});
219+
171220
// Analyze entire page for accessibility issues
172221
testA11y('ds-item-collection-mapper');
173222

package.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dspace-angular",
3-
"version": "8.2.0",
3+
"version": "8.3.0",
44
"scripts": {
55
"ng": "ng",
66
"config:watch": "nodemon",
@@ -67,7 +67,7 @@
6767
"@angular/platform-server": "^17.3.11",
6868
"@angular/router": "^17.3.11",
6969
"@angular/ssr": "^17.3.17",
70-
"@babel/runtime": "7.27.6",
70+
"@babel/runtime": "7.28.4",
7171
"@kolkov/ngx-gallery": "^2.0.1",
7272
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
7373
"@ng-dynamic-forms/core": "^16.0.0",
@@ -78,14 +78,14 @@
7878
"@ngx-translate/core": "^14.0.0",
7979
"@nicky-lenaers/ngx-scroll-to": "^14.0.0",
8080
"angulartics2": "^12.2.0",
81-
"axios": "^1.10.0",
81+
"axios": "^1.13.2",
8282
"bootstrap": "^4.6.1",
8383
"cerialize": "0.1.18",
8484
"cli-progress": "^3.12.0",
8585
"colors": "^1.4.0",
86-
"compression": "^1.8.0",
86+
"compression": "^1.8.1",
8787
"cookie-parser": "1.4.7",
88-
"core-js": "^3.42.0",
88+
"core-js": "^3.47.0",
8989
"date-fns": "^2.29.3",
9090
"date-fns-tz": "^1.3.7",
9191
"deepmerge": "^4.3.1",
@@ -96,9 +96,9 @@
9696
"filesize": "^6.1.0",
9797
"http-proxy-middleware": "^2.0.9",
9898
"http-terminator": "^3.2.0",
99-
"isbot": "^5.1.28",
99+
"isbot": "^5.1.32",
100100
"js-cookie": "2.2.1",
101-
"js-yaml": "^4.1.0",
101+
"js-yaml": "^4.1.1",
102102
"json5": "^2.2.3",
103103
"jsonschema": "1.5.0",
104104
"jwt-decode": "^3.1.2",
@@ -109,7 +109,7 @@
109109
"mirador": "^3.4.3",
110110
"mirador-dl-plugin": "^0.13.0",
111111
"mirador-share-plugin": "^0.16.0",
112-
"morgan": "^1.10.0",
112+
"morgan": "^1.10.1",
113113
"ng2-file-upload": "9.0.0",
114114
"ng2-nouislider": "^2.0.0",
115115
"ngx-infinite-scroll": "^16.0.0",
@@ -147,28 +147,28 @@
147147
"@types/grecaptcha": "^3.0.9",
148148
"@types/jasmine": "~3.6.0",
149149
"@types/js-cookie": "2.2.6",
150-
"@types/lodash": "^4.17.17",
150+
"@types/lodash": "^4.17.21",
151151
"@types/node": "^14.14.9",
152152
"@typescript-eslint/eslint-plugin": "^7.2.0",
153153
"@typescript-eslint/parser": "^7.2.0",
154154
"@typescript-eslint/rule-tester": "^7.2.0",
155155
"@typescript-eslint/utils": "^7.2.0",
156-
"axe-core": "^4.10.3",
156+
"axe-core": "^4.11.0",
157157
"compression-webpack-plugin": "^9.2.0",
158158
"copy-webpack-plugin": "^6.4.1",
159159
"cross-env": "^7.0.3",
160-
"csstype": "^3.1.3",
160+
"csstype": "^3.2.3",
161161
"cypress": "^13.17.0",
162-
"cypress-axe": "^1.6.0",
162+
"cypress-axe": "^1.7.0",
163163
"deep-freeze": "0.0.1",
164164
"eslint": "^8.39.0",
165165
"eslint-plugin-deprecation": "^1.4.1",
166166
"eslint-plugin-dspace-angular-html": "link:./lint/dist/src/rules/html",
167167
"eslint-plugin-dspace-angular-ts": "link:./lint/dist/src/rules/ts",
168-
"eslint-plugin-import": "^2.31.0",
168+
"eslint-plugin-import": "^2.32.0",
169169
"eslint-plugin-import-newlines": "^1.3.1",
170170
"eslint-plugin-jsdoc": "^45.0.0",
171-
"eslint-plugin-jsonc": "^2.20.1",
171+
"eslint-plugin-jsonc": "^2.21.0",
172172
"eslint-plugin-lodash": "^7.4.0",
173173
"eslint-plugin-rxjs": "^5.0.3",
174174
"eslint-plugin-simple-import-sort": "^10.0.0",
@@ -183,7 +183,7 @@
183183
"karma-jasmine": "~4.0.0",
184184
"karma-jasmine-html-reporter": "^1.5.0",
185185
"karma-mocha-reporter": "2.2.5",
186-
"ng-mocks": "^14.13.5",
186+
"ng-mocks": "^14.14.0",
187187
"ngx-mask": "14.2.4",
188188
"nodemon": "^2.0.22",
189189
"postcss": "^8.5",
@@ -195,13 +195,13 @@
195195
"react-copy-to-clipboard": "^5.1.0",
196196
"react-dom": "^16.14.0",
197197
"rimraf": "^3.0.2",
198-
"sass": "~1.89.2",
198+
"sass": "~1.94.2",
199199
"sass-loader": "^12.6.0",
200200
"sass-resources-loader": "^2.2.5",
201201
"ts-node": "^8.10.2",
202202
"typescript": "~5.4.5",
203-
"webpack": "5.99.9",
203+
"webpack": "5.101.0",
204204
"webpack-cli": "^5.1.4",
205-
"webpack-dev-server": "^4.15.1"
205+
"webpack-dev-server": "^5.2.2"
206206
}
207207
}

scripts/sync-i18n-files.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ function parseCliInput() {
3838
.usage('([-d <output-dir>] [-s <source-file>]) || (-t <target-file> (-i | -o <output>) [-s <source-file>])')
3939
.parse(process.argv);
4040

41-
if (!program.targetFile) {
41+
const sourceFile = program.opts().sourceFile;
42+
43+
if (!program.targetFile) {
4244
fs.readdirSync(projectRoot(LANGUAGE_FILES_LOCATION)).forEach(file => {
43-
if (!program.sourceFile.toString().endsWith(file)) {
45+
if (!sourceFile.toString().endsWith(file)) {
4446
const targetFileLocation = projectRoot(LANGUAGE_FILES_LOCATION + "/" + file);
45-
console.log('Syncing file at: ' + targetFileLocation + ' with source file at: ' + program.sourceFile);
47+
console.log('Syncing file at: ' + targetFileLocation + ' with source file at: ' + sourceFile);
4648
if (program.outputDir) {
4749
if (!fs.existsSync(program.outputDir)) {
4850
fs.mkdirSync(program.outputDir);
@@ -67,7 +69,7 @@ function parseCliInput() {
6769
console.log(program.outputHelp());
6870
process.exit(1);
6971
}
70-
if (!checkIfFileExists(program.sourceFile)) {
72+
if (!checkIfFileExists(sourceFile)) {
7173
console.error('Path of source file is not valid.');
7274
console.log(program.outputHelp());
7375
process.exit(1);
@@ -101,7 +103,7 @@ function syncFileWithSource(pathToTargetFile, pathToOutputFile) {
101103
targetLines.push(line.trim());
102104
}));
103105
progressBar.update(10);
104-
const sourceFile = readFileIfExists(program.sourceFile);
106+
const sourceFile = readFileIfExists(program.opts().sourceFile);
105107
sourceFile.toString().split("\n").forEach((function (line) {
106108
sourceLines.push(line.trim());
107109
}));

server.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
269269
],
270270
})
271271
.then((html) => {
272+
// If headers were already sent, then do nothing else, it is probably a
273+
// redirect response
274+
if (res.headersSent) {
275+
return;
276+
}
277+
272278
if (hasValue(html)) {
273279
// Replace REST URL with UI URL
274280
if (environment.ssr.replaceRestUrl && REST_BASE_URL !== environment.rest.baseUrl) {
@@ -304,13 +310,24 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
304310
});
305311
}
306312

307-
/**
308-
* Send back response to user to trigger direct client-side rendering (CSR)
309-
* @param req current request
310-
* @param res current response
311-
*/
313+
// Read file once at startup
314+
const indexHtmlContent = readFileSync(indexHtml, 'utf8');
315+
312316
function clientSideRender(req, res) {
313-
res.sendFile(indexHtml);
317+
const namespace = environment.ui.nameSpace || '/';
318+
let html = indexHtmlContent;
319+
// Replace base href dynamically
320+
html = html.replace(
321+
/<base href="[^"]*">/,
322+
`<base href="${namespace.endsWith('/') ? namespace : namespace + '/'}">`
323+
);
324+
325+
// Replace REST URL with UI URL
326+
if (environment.ssr.replaceRestUrl && REST_BASE_URL !== environment.rest.baseUrl) {
327+
html = html.replace(new RegExp(REST_BASE_URL, 'g'), environment.rest.baseUrl);
328+
}
329+
330+
res.send(html);
314331
}
315332

316333

@@ -561,8 +578,8 @@ function createHttpsServer(keys) {
561578
* Create an HTTP server with the configured port and host.
562579
*/
563580
function run() {
564-
const port = environment.ui.port || 4000;
565-
const host = environment.ui.host || '/';
581+
const port = environment.ui.port;
582+
const host = environment.ui.host;
566583

567584
// Start up the Node server
568585
const server = app();

src/app/app-routes.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routin
2525
import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths';
2626
import { authBlockingGuard } from './core/auth/auth-blocking.guard';
2727
import { authenticatedGuard } from './core/auth/authenticated.guard';
28+
import { notAuthenticatedGuard } from './core/auth/not-authenticated.guard';
2829
import { groupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
2930
import { siteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
3031
import { siteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard';
@@ -98,13 +99,13 @@ export const APP_ROUTES: Route[] = [
9899
path: REGISTER_PATH,
99100
loadChildren: () => import('./register-page/register-page-routes')
100101
.then((m) => m.ROUTES),
101-
canActivate: [siteRegisterGuard],
102+
canActivate: [notAuthenticatedGuard, siteRegisterGuard],
102103
},
103104
{
104105
path: FORGOT_PASSWORD_PATH,
105106
loadChildren: () => import('./forgot-password/forgot-password-routes')
106107
.then((m) => m.ROUTES),
107-
canActivate: [endUserAgreementCurrentUserGuard, forgotPasswordCheckGuard],
108+
canActivate: [notAuthenticatedGuard, endUserAgreementCurrentUserGuard, forgotPasswordCheckGuard],
108109
},
109110
{
110111
path: COMMUNITY_MODULE_PATH,
@@ -178,11 +179,13 @@ export const APP_ROUTES: Route[] = [
178179
path: 'login',
179180
loadChildren: () => import('./login-page/login-page-routes')
180181
.then((m) => m.ROUTES),
182+
canActivate: [notAuthenticatedGuard],
181183
},
182184
{
183185
path: 'logout',
184186
loadChildren: () => import('./logout-page/logout-page-routes')
185187
.then((m) => m.ROUTES),
188+
canActivate: [authenticatedGuard],
186189
},
187190
{
188191
path: 'submit',

src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class CollectionItemMapperComponent implements OnInit {
161161

162162
this.collectionName$ = this.collectionRD$.pipe(
163163
map((rd: RemoteData<Collection>) => {
164-
return this.dsoNameService.getName(rd.payload);
164+
return this.dsoNameService.getName(rd.payload, true);
165165
}),
166166
);
167167
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;

src/app/core/auth/models/auth.method-type.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export enum AuthMethodType {
33
Shibboleth = 'shibboleth',
44
Ldap = 'ldap',
55
Ip = 'ip',
6-
X509 = 'x509',
76
Oidc = 'oidc',
87
Orcid = 'orcid'
98
}

src/app/core/auth/models/auth.method.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ export class AuthMethod {
2222
this.location = location;
2323
break;
2424
}
25-
case 'x509': {
26-
this.authMethodType = AuthMethodType.X509;
27-
break;
28-
}
2925
case 'password': {
3026
this.authMethodType = AuthMethodType.Password;
3127
break;

0 commit comments

Comments
 (0)