Skip to content

Commit 9982e4d

Browse files
committed
Merge tag 'dspace-8.3' into DS-test-8.3-merge-into-uoemainlibrary-dspace8x
# Conflicts: # package.json # src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html # src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts # src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts # src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html # src/assets/i18n/ar.json5 # src/assets/i18n/bn.json5 # src/assets/i18n/ca.json5 # src/assets/i18n/cs.json5 # src/assets/i18n/de.json5 # src/assets/i18n/el.json5 # src/assets/i18n/en.json5 # src/assets/i18n/es.json5 # src/assets/i18n/fi.json5 # src/assets/i18n/fr.json5 # src/assets/i18n/gd.json5 # src/assets/i18n/hi.json5 # src/assets/i18n/hu.json5 # src/assets/i18n/it.json5 # src/assets/i18n/ja.json5 # src/assets/i18n/kk.json5 # src/assets/i18n/lv.json5 # src/assets/i18n/nl.json5 # src/assets/i18n/pl.json5 # src/assets/i18n/pt-BR.json5 # src/assets/i18n/pt-PT.json5 # src/assets/i18n/sr-cyr.json5 # src/assets/i18n/sr-lat.json5 # src/assets/i18n/sv.json5 # src/assets/i18n/sw.json5 # src/assets/i18n/tr.json5 # src/assets/i18n/uk.json5 # src/assets/i18n/vi.json5 # src/themes/custom/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts # yarn.lock
2 parents 34e5861 + 67f6716 commit 9982e4d

110 files changed

Lines changed: 3104 additions & 1911 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: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ jobs:
4040
# Initializes the CodeQL tools for scanning.
4141
# https://github.com/github/codeql-action
4242
- name: Initialize CodeQL
43-
uses: github/codeql-action/init@v2
43+
uses: github/codeql-action/init@v3
4444
with:
4545
languages: javascript
4646

4747
# Autobuild attempts to build any compiled languages
4848
- name: Autobuild
49-
uses: github/codeql-action/autobuild@v2
49+
uses: github/codeql-action/autobuild@v3
5050

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

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": "5.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;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import {
3+
ActivatedRouteSnapshot,
4+
RouterStateSnapshot,
5+
} from '@angular/router';
6+
import {
7+
firstValueFrom,
8+
of,
9+
} from 'rxjs';
10+
import { PAGE_NOT_FOUND_PATH } from 'src/app/app-routing-paths';
11+
12+
import { HardRedirectService } from '../services/hard-redirect.service';
13+
import { AuthService } from './auth.service';
14+
import { notAuthenticatedGuard } from './not-authenticated.guard';
15+
16+
describe('notAuthenticatedGuard', () => {
17+
let authService: jasmine.SpyObj<AuthService>;
18+
let hardRedirectService: jasmine.SpyObj<HardRedirectService>;
19+
const mockRoute = {} as ActivatedRouteSnapshot;
20+
const mockState = {} as RouterStateSnapshot;
21+
22+
beforeEach(() => {
23+
const authSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated']);
24+
const redirectSpy = jasmine.createSpyObj('HardRedirectService', ['redirect']);
25+
26+
TestBed.configureTestingModule({
27+
providers: [
28+
{ provide: AuthService, useValue: authSpy },
29+
{ provide: HardRedirectService, useValue: redirectSpy },
30+
],
31+
});
32+
33+
authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;
34+
hardRedirectService = TestBed.inject(HardRedirectService) as jasmine.SpyObj<HardRedirectService>;
35+
});
36+
37+
it('should block access and redirect if user is logged in', async () => {
38+
authService.isAuthenticated.and.returnValue(of(true));
39+
40+
const result$ = TestBed.runInInjectionContext(() =>
41+
notAuthenticatedGuard(mockRoute, mockState),
42+
);
43+
44+
const result = await firstValueFrom(result$ as any);
45+
expect(result).toBe(false);
46+
expect(hardRedirectService.redirect).toHaveBeenCalledWith(PAGE_NOT_FOUND_PATH);
47+
});
48+
49+
it('should allow access if user is not logged in', async () => {
50+
authService.isAuthenticated.and.returnValue(of(false));
51+
52+
const result$ = TestBed.runInInjectionContext(() =>
53+
notAuthenticatedGuard(mockRoute, mockState),
54+
);
55+
56+
const result = await firstValueFrom(result$ as any);
57+
expect(result).toBe(true);
58+
expect(hardRedirectService.redirect).not.toHaveBeenCalled();
59+
});
60+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { inject } from '@angular/core';
2+
import { CanActivateFn } from '@angular/router';
3+
import { map } from 'rxjs/operators';
4+
import { PAGE_NOT_FOUND_PATH } from 'src/app/app-routing-paths';
5+
6+
import { HardRedirectService } from '../services/hard-redirect.service';
7+
import { AuthService } from './auth.service';
8+
9+
export const notAuthenticatedGuard: CanActivateFn = () => {
10+
const authService = inject(AuthService);
11+
const redirectService = inject(HardRedirectService);
12+
13+
return authService.isAuthenticated().pipe(
14+
map((isLoggedIn) => {
15+
if (isLoggedIn) {
16+
redirectService.redirect(PAGE_NOT_FOUND_PATH);
17+
return false;
18+
}
19+
20+
return true;
21+
}),
22+
);
23+
};

0 commit comments

Comments
 (0)