From edfaee6aab58bcb3b1f3d122d3b70276b0c0d8fa Mon Sep 17 00:00:00 2001
From: Alexandre Vryghem
Date: Tue, 10 Dec 2024 13:54:57 +0100
Subject: [PATCH 01/54] 119276: Fixed search service first emitting cached
stale values instead of waiting for non-stale response
This was problematic for the places that used getFist operators. This is because they only emit data once, and the first value could be the old cached value
---
src/app/core/shared/search/search.service.ts | 21 +++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts
index a88a8b0d16..d9fd85f9a2 100644
--- a/src/app/core/shared/search/search.service.ts
+++ b/src/app/core/shared/search/search.service.ts
@@ -1,7 +1,7 @@
/* eslint-disable max-classes-per-file */
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core';
-import { map, switchMap, take } from 'rxjs/operators';
+import { map, switchMap, take, skipWhile } from 'rxjs/operators';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { ResponseParsingService } from '../../data/parsing.service';
import { RemoteData } from '../../data/remote-data';
@@ -140,6 +140,7 @@ export class SearchService implements OnDestroy {
search(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable>> {
const href$ = this.getEndpoint(searchOptions);
+ let startTime: number;
href$.pipe(
take(1),
map((href: string) => {
@@ -163,6 +164,7 @@ export class SearchService implements OnDestroy {
searchOptions: searchOptions
});
+ startTime = new Date().getTime();
this.requestService.send(request, useCachedVersionIfAvailable);
});
@@ -170,7 +172,13 @@ export class SearchService implements OnDestroy {
switchMap((href: string) => this.rdb.buildFromHref>(href))
);
- return this.directlyAttachIndexableObjects(sqr$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
+ return this.directlyAttachIndexableObjects(sqr$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe(
+ // This skip ensures that if a stale object is present in the cache when you do a
+ // call it isn't immediately returned, but we wait until the remote data for the new request
+ // is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
+ // cached completed object
+ skipWhile((rd: RemoteData>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
+ );
}
/**
@@ -291,9 +299,16 @@ export class SearchService implements OnDestroy {
return FacetValueResponseParsingService;
}
});
+ const startTime = new Date().getTime();
this.requestService.send(request, useCachedVersionIfAvailable);
- return this.rdb.buildFromHref(href);
+ return this.rdb.buildFromHref(href).pipe(
+ // This skip ensures that if a stale object is present in the cache when you do a
+ // call it isn't immediately returned, but we wait until the remote data for the new request
+ // is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
+ // cached completed object
+ skipWhile((rd: RemoteData) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
+ );
}
/**
From 58ff240c0525ce71c73046382f66cff458c970ad Mon Sep 17 00:00:00 2001
From: Alexandre Vryghem
Date: Thu, 23 Jan 2025 19:59:58 +0100
Subject: [PATCH 02/54] 119276: Added tests to prove that cached stale requests
are not emitted
Also cleaned up test class
---
.../core/shared/search/search.service.spec.ts | 494 +++++++++++-------
1 file changed, 305 insertions(+), 189 deletions(-)
diff --git a/src/app/core/shared/search/search.service.spec.ts b/src/app/core/shared/search/search.service.spec.ts
index fe5b495ab0..e1060c9dc8 100644
--- a/src/app/core/shared/search/search.service.spec.ts
+++ b/src/app/core/shared/search/search.service.spec.ts
@@ -1,216 +1,332 @@
-import { TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
import { CommonModule } from '@angular/common';
-import { Component } from '@angular/core';
-import { SearchService } from './search.service';
-import { Router, UrlTree } from '@angular/router';
-import { RequestService } from '../../data/request.service';
-import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub';
-import { RouterStub } from '../../../shared/testing/router.stub';
-import { HALEndpointService } from '../hal-endpoint.service';
-import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
-import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model';
-import { RemoteData } from '../../data/remote-data';
-import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
-import { CommunityDataService } from '../../data/community-data.service';
-import { ViewMode } from '../view-mode.model';
-import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
-import { map } from 'rxjs/operators';
-import { RouteService } from '../../services/route.service';
-import { routeServiceStub } from '../../../shared/testing/route-service.stub';
-import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
-import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
-import { SearchObjects } from '../../../shared/search/models/search-objects.model';
-import { PaginationService } from '../../pagination/pagination.service';
-import { SearchConfigurationService } from './search-configuration.service';
-import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
-import { RequestEntry } from '../../data/request-entry.model';
+import { TestBed } from '@angular/core/testing';
+import { RouterModule } from '@angular/router';
import { Angulartics2 } from 'angulartics2';
-import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model';
-import anything = jasmine.anything;
+import { of as observableOf } from 'rxjs';
+import { TestScheduler } from 'rxjs/testing';
-@Component({ template: '' })
-class DummyComponent {
-}
+import { environment } from '../../../../environments/environment.test';
+import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock';
+import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
+import { FacetValues } from '../../../shared/search/models/facet-values.model';
+import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model';
+import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model';
+import { SearchObjects } from '../../../shared/search/models/search-objects.model';
+import { HALEndpointServiceStub } from '../../../shared/testing/hal-endpoint-service.stub';
+import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
+import { routeServiceStub } from '../../../shared/testing/route-service.stub';
+import { SearchConfigurationServiceStub } from '../../../shared/testing/search-configuration-service.stub';
+import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
+import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
+import { RemoteData } from '../../data/remote-data';
+import { RequestService } from '../../data/request.service';
+import { RequestEntryState } from '../../data/request-entry-state.model';
+import { PaginationService } from '../../pagination/pagination.service';
+import { RouteService } from '../../services/route.service';
+import { HALEndpointService } from '../hal-endpoint.service';
+import { ViewMode } from '../view-mode.model';
+import { SearchService } from './search.service';
+import { SearchConfigurationService } from './search-configuration.service';
+import anything = jasmine.anything;
+import SpyObj = jasmine.SpyObj;
describe('SearchService', () => {
- describe('By default', () => {
- let searchService: SearchService;
- const router = new RouterStub();
- const route = new ActivatedRouteStub();
- const searchConfigService = { paginationID: 'page-id' };
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [
- CommonModule,
- RouterTestingModule.withRoutes([
- { path: 'search', component: DummyComponent, pathMatch: 'full' },
- ])
- ],
- declarations: [
- DummyComponent
- ],
- providers: [
- { provide: Router, useValue: router },
- { provide: RouteService, useValue: routeServiceStub },
- { provide: RequestService, useValue: getMockRequestService() },
- { provide: RemoteDataBuildService, useValue: {} },
- { provide: HALEndpointService, useValue: {} },
- { provide: CommunityDataService, useValue: {} },
- { provide: DSpaceObjectDataService, useValue: {} },
- { provide: PaginationService, useValue: {} },
- { provide: SearchConfigurationService, useValue: searchConfigService },
- { provide: Angulartics2, useValue: {} },
- SearchService
- ],
- });
- searchService = TestBed.inject(SearchService);
- });
+ let service: SearchService;
- it('should return list view mode', () => {
- searchService.getViewMode().subscribe((viewMode) => {
- expect(viewMode).toBe(ViewMode.ListElement);
- });
+ let halService: HALEndpointServiceStub;
+ let paginationService: PaginationServiceStub;
+ let remoteDataBuildService: RemoteDataBuildService;
+ let requestService: SpyObj;
+ let routeService: RouteService;
+ let searchConfigService: SearchConfigurationServiceStub;
+
+ let testScheduler: TestScheduler;
+ let msToLive: number;
+ let remoteDataTimestamp: number;
+
+ beforeEach(() => {
+ halService = new HALEndpointServiceStub(environment.rest.baseUrl);
+ paginationService = new PaginationServiceStub();
+ remoteDataBuildService = getMockRemoteDataBuildService();
+ requestService = getMockRequestService();
+ searchConfigService = new SearchConfigurationServiceStub();
+
+ initTestData();
+
+ TestBed.configureTestingModule({
+ imports: [
+ CommonModule,
+ RouterModule.forRoot([]),
+ ],
+ providers: [
+ { provide: RouteService, useValue: routeServiceStub },
+ { provide: RequestService, useValue: requestService },
+ { provide: RemoteDataBuildService, useValue: remoteDataBuildService },
+ { provide: HALEndpointService, useValue: halService },
+ { provide: DSpaceObjectDataService, useValue: {} },
+ { provide: PaginationService, useValue: paginationService },
+ { provide: SearchConfigurationService, useValue: searchConfigService },
+ { provide: Angulartics2, useValue: {} },
+ SearchService,
+ ],
});
+ service = TestBed.inject(SearchService);
+ routeService = TestBed.inject(RouteService);
});
- describe('', () => {
- let searchService: SearchService;
- const router = new RouterStub();
- let routeService;
- const halService = {
- /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
- getEndpoint: () => {
- }
- /* eslint-enable no-empty,@typescript-eslint/no-empty-function */
-
- };
-
- const remoteDataBuildService = {
- toRemoteDataObservable: (requestEntryObs: Observable, payloadObs: Observable) => {
- return observableCombineLatest([requestEntryObs, payloadObs]).pipe(
- map(([req, pay]) => {
- return { req, pay };
- })
- );
- },
- aggregate: (input: Observable>[]): Observable> => {
- return createSuccessfulRemoteDataObject$([]);
- },
- buildFromHref: (href: string): Observable> => {
- return createSuccessfulRemoteDataObject$(Object.assign(new SearchObjects(), {
- page: []
- }));
- }
- };
-
- const paginationService = new PaginationServiceStub();
- const searchConfigService = { paginationID: 'page-id' };
- const requestService = getMockRequestService();
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [
- CommonModule,
- RouterTestingModule.withRoutes([
- { path: 'search', component: DummyComponent, pathMatch: 'full' },
- ])
- ],
- declarations: [
- DummyComponent
- ],
- providers: [
- { provide: Router, useValue: router },
- { provide: RouteService, useValue: routeServiceStub },
- { provide: RequestService, useValue: requestService },
- { provide: RemoteDataBuildService, useValue: remoteDataBuildService },
- { provide: HALEndpointService, useValue: halService },
- { provide: CommunityDataService, useValue: {} },
- { provide: DSpaceObjectDataService, useValue: {} },
- { provide: PaginationService, useValue: paginationService },
- { provide: SearchConfigurationService, useValue: searchConfigService },
- { provide: Angulartics2, useValue: {} },
- SearchService
- ],
- });
- searchService = TestBed.inject(SearchService);
- routeService = TestBed.inject(RouteService);
- const urlTree = Object.assign(new UrlTree(), { root: { children: { primary: 'search' } } });
- router.parseUrl.and.returnValue(urlTree);
+ function initTestData(): void {
+ testScheduler = new TestScheduler((actual, expected) => {
+ expect(actual).toEqual(expected);
});
+ msToLive = 15 * 60 * 1000;
+ // The response's lastUpdated equals the time of 60 seconds after the test started, ensuring they are not perceived
+ // as cached values.
+ remoteDataTimestamp = new Date().getTime() + 60 * 1000;
+ }
+
+ describe('setViewMode', () => {
it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
- searchService.setViewMode(ViewMode.ListElement);
- expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], { page: 1 }, { view: ViewMode.ListElement }
- );
+ service.setViewMode(ViewMode.ListElement);
+
+ expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('test-id', ['/search'], { page: 1 }, { view: ViewMode.ListElement });
});
it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
- searchService.setViewMode(ViewMode.GridElement);
- expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], { page: 1 }, { view: ViewMode.GridElement }
- );
+ service.setViewMode(ViewMode.GridElement);
+
+ expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('test-id', ['/search'], { page: 1 }, { view: ViewMode.GridElement });
+ });
+ });
+
+ describe('getViewMode', () => {
+ it('should return list view mode', () => {
+ testScheduler.run(({ expectObservable }) => {
+ expectObservable(service.getViewMode()).toBe('(a|)', {
+ a: ViewMode.ListElement,
+ });
+ });
});
it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => {
- let viewMode = ViewMode.GridElement;
- spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
- ['view', ViewMode.ListElement],
- ])));
+ testScheduler.run(({ expectObservable }) => {
+ spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
+ ['view', ViewMode.ListElement],
+ ])));
- searchService.getViewMode().subscribe((mode) => viewMode = mode);
- expect(viewMode).toEqual(ViewMode.ListElement);
+ expectObservable(service.getViewMode()).toBe('(a|)', {
+ a: ViewMode.ListElement,
+ });
+ });
});
it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => {
- let viewMode = ViewMode.ListElement;
- spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
- ['view', ViewMode.GridElement],
- ])));
- searchService.getViewMode().subscribe((mode) => viewMode = mode);
- expect(viewMode).toEqual(ViewMode.GridElement);
- });
+ testScheduler.run(({ expectObservable }) => {
+ spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
+ ['view', ViewMode.GridElement],
+ ])));
- describe('when search is called', () => {
- const endPoint = 'http://endpoint.com/test/test';
- const searchOptions = new PaginatedSearchOptions({});
- beforeEach(() => {
- spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
- spyOn((searchService as any).rdb, 'buildFromHref').and.callThrough();
- /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
- searchService.search(searchOptions).subscribe((t) => {
- }); // subscribe to make sure all methods are called
- /* eslint-enable no-empty,@typescript-eslint/no-empty-function */
- });
-
- it('should call getEndpoint on the halService', () => {
- expect((searchService as any).halService.getEndpoint).toHaveBeenCalled();
- });
-
- it('should send out the request on the request service', () => {
- expect((searchService as any).requestService.send).toHaveBeenCalled();
- });
-
- it('should call getByHref on the request service with the correct request url', () => {
- expect((searchService as any).rdb.buildFromHref).toHaveBeenCalledWith(endPoint);
- });
- });
-
- describe('when getFacetValuesFor is called with a filterQuery', () => {
- it('should add the encoded filterQuery to the args list', () => {
- jasmine.getEnv().allowRespy(true);
- const spyRequest = spyOn((searchService as any), 'request').and.stub();
- spyOn(requestService, 'send').and.returnValue(true);
- const searchFilterConfig = new SearchFilterConfig();
- searchFilterConfig._links = {
- self: {
- href: 'https://demo.dspace.org/',
- },
- };
-
- searchService.getFacetValuesFor(searchFilterConfig, 1, undefined, 'filter&Query');
-
- expect(spyRequest).toHaveBeenCalledWith(anything(), 'https://demo.dspace.org?page=0&size=5&prefix=filter%26Query');
+ expectObservable(service.getViewMode()).toBe('(a|)', {
+ a: ViewMode.GridElement,
+ });
});
});
});
+
+ describe('search', () => {
+ let remoteDataMocks: Record>>;
+
+ beforeEach(() => {
+ remoteDataMocks = {
+ RequestPending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
+ ResponsePending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
+ Success: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Success, undefined, new SearchObjects(), 200),
+ SuccessStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.SuccessStale, undefined, new SearchObjects(), 200),
+ };
+ });
+
+ describe('when useCachedVersionIfAvailable is true', () => {
+ it(`should emit a cached completed RemoteData immediately, and keep emitting if it gets re-requested`, () => {
+ testScheduler.run(({ cold, expectObservable }) => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.returnValue(cold('a-b-c-d-e', {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ }));
+ const expected = 'a-b-c-d-e';
+ const values = {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ };
+
+ expectObservable(service.search(undefined, msToLive, true)).toBe(expected, values);
+ });
+ });
+ });
+
+ describe('when useCachedVersionIfAvailable is false', () => {
+ it('should not emit a cached completed RemoteData', () => {
+ // Old cached value from 1 minute before the test started
+ const oldCachedSucceededData: RemoteData> = Object.assign(new SearchObjects(), remoteDataMocks.Success, {
+ timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
+ lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
+ });
+ testScheduler.run(({ cold, expectObservable }) => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.returnValue(cold('a-b-c-d-e', {
+ a: oldCachedSucceededData,
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ }));
+ const expected = '--b-c-d-e';
+ const values = {
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ };
+
+ expectObservable(service.search(undefined, msToLive, false)).toBe(expected, values);
+ });
+ });
+
+ it('should emit the first completed RemoteData since the request was made', () => {
+ testScheduler.run(({ cold, expectObservable }) => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.returnValue(cold('a-b', {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.SuccessStale,
+ }));
+ const expected = 'a-b';
+ const values = {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.SuccessStale,
+ };
+ expectObservable(service.search(undefined, msToLive, false)).toBe(expected, values);
+ });
+ });
+ });
+
+ it('should call getEndpoint on the halService', () => {
+ spyOn(halService, 'getEndpoint').and.callThrough();
+
+ service.search(new PaginatedSearchOptions({})).subscribe();
+
+ expect(halService.getEndpoint).toHaveBeenCalled();
+ });
+
+ it('should send out the request on the request service', () => {
+ service.search(new PaginatedSearchOptions({})).subscribe();
+
+ expect(requestService.send).toHaveBeenCalled();
+ });
+
+ it('should call getByHref on the request service with the correct request url', () => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.callThrough();
+
+ service.search(new PaginatedSearchOptions({})).subscribe();
+
+ expect(remoteDataBuildService.buildFromHref).toHaveBeenCalledWith(environment.rest.baseUrl + '/discover/search/objects');
+ });
+ });
+
+ describe('getFacetValuesFor', () => {
+ let remoteDataMocks: Record>;
+ let filterConfig: SearchFilterConfig;
+
+ beforeEach(() => {
+ remoteDataMocks = {
+ RequestPending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
+ ResponsePending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
+ Success: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Success, undefined, new FacetValues(), 200),
+ SuccessStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.SuccessStale, undefined, new FacetValues(), 200),
+ };
+ filterConfig = new SearchFilterConfig();
+ filterConfig._links = {
+ self: {
+ href: environment.rest.baseUrl,
+ },
+ };
+ });
+
+ describe('when useCachedVersionIfAvailable is true', () => {
+ it(`should emit a cached completed RemoteData immediately, and keep emitting if it gets re-requested`, () => {
+ testScheduler.run(({ cold, expectObservable }) => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.returnValue(cold('a-b-c-d-e', {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ }));
+ const expected = 'a-b-c-d-e';
+ const values = {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ };
+
+ expectObservable(service.getFacetValuesFor(filterConfig, 1, undefined, undefined, true)).toBe(expected, values);
+ });
+ });
+ });
+
+ describe('when useCachedVersionIfAvailable is false', () => {
+ it('should not emit a cached completed RemoteData', () => {
+ // Old cached value from 1 minute before the test started
+ const oldCachedSucceededData: RemoteData = Object.assign(new FacetValues(), remoteDataMocks.Success, {
+ timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
+ lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
+ });
+ testScheduler.run(({ cold, expectObservable }) => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.returnValue(cold('a-b-c-d-e', {
+ a: oldCachedSucceededData,
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ }));
+ const expected = '--b-c-d-e';
+ const values = {
+ b: remoteDataMocks.RequestPending,
+ c: remoteDataMocks.ResponsePending,
+ d: remoteDataMocks.Success,
+ e: remoteDataMocks.SuccessStale,
+ };
+
+ expectObservable(service.getFacetValuesFor(filterConfig, 1, undefined, undefined, false)).toBe(expected, values);
+ });
+ });
+
+ it('should emit the first completed RemoteData since the request was made', () => {
+ testScheduler.run(({ cold, expectObservable }) => {
+ spyOn(remoteDataBuildService, 'buildFromHref').and.returnValue(cold('a-b', {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.SuccessStale,
+ }));
+ const expected = 'a-b';
+ const values = {
+ a: remoteDataMocks.Success,
+ b: remoteDataMocks.SuccessStale,
+ };
+ expectObservable(service.getFacetValuesFor(filterConfig, 1, undefined, undefined, false)).toBe(expected, values);
+ });
+ });
+ });
+
+ it('should encode the filterQuery', () => {
+ spyOn((service as any), 'request').and.callThrough();
+
+ service.getFacetValuesFor(filterConfig, 1, undefined, 'filter&Query');
+
+ expect((service as any).request).toHaveBeenCalledWith(anything(), environment.rest.baseUrl + '?page=0&size=5&prefix=filter%26Query');
+ });
+ });
});
From b69b21af6c558f2fa4f2f694d3f2cb713ea625a6 Mon Sep 17 00:00:00 2001
From: Jens Vannerum
Date: Mon, 20 Jan 2025 16:45:36 +0100
Subject: [PATCH 03/54] 119612: UI warning that only first part of configured
items will be exported
---
.../search-export-csv.component.html | 17 ++++++++--
.../search-export-csv.component.ts | 34 ++++++++++++++++++-
.../search-results.component.html | 3 +-
src/assets/i18n/en.json5 | 2 ++
4 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.html b/src/app/shared/search/search-export-csv/search-export-csv.component.html
index 7bf8704300..35ef89b728 100644
--- a/src/app/shared/search/search-export-csv/search-export-csv.component.html
+++ b/src/app/shared/search/search-export-csv/search-export-csv.component.html
@@ -1,7 +1,20 @@
+
+
+
{{tooltipMsg | translate}}
+
+
+
+
+
+
{{tooltipMsg | translate}}
+
{{exportLimitExceededMsg}}
+
+
+
-
\ No newline at end of file
+
diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.ts
index f8ec2012b1..8b6252ada7 100644
--- a/src/app/shared/search/search-export-csv/search-export-csv.component.ts
+++ b/src/app/shared/search/search-export-csv/search-export-csv.component.ts
@@ -21,10 +21,12 @@ import {
switchMap,
} from 'rxjs/operators';
+import { ConfigurationDataService } from '../../../core/data/configuration-data.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { RemoteData } from '../../../core/data/remote-data';
+import { ConfigurationProperty } from '../../../core/shared/configuration-property.model';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths';
import { Process } from '../../../process-page/processes/process.model';
@@ -53,6 +55,11 @@ export class SearchExportCsvComponent implements OnInit {
*/
@Input() searchConfig: PaginatedSearchOptions;
+ /**
+ * The total number of items in the search results which can be exported
+ */
+ @Input() total: number;
+
/**
* Observable used to determine whether the button should be shown
*/
@@ -63,12 +70,18 @@ export class SearchExportCsvComponent implements OnInit {
*/
tooltipMsg = 'metadata-export-search.tooltip';
+ exportLimitExceededKey = 'metadata-export-search.submit.error.limit-exceeded';
+
+ exportLimitExceededMsg = '';
+
+ shouldShowWarning$: Observable;
+
constructor(private scriptDataService: ScriptDataService,
private authorizationDataService: AuthorizationDataService,
private notificationsService: NotificationsService,
private translateService: TranslateService,
private router: Router,
- ) {
+ private configurationService: ConfigurationDataService) {
}
ngOnInit(): void {
@@ -78,6 +91,25 @@ export class SearchExportCsvComponent implements OnInit {
map((canExecute: boolean) => canExecute),
startWith(false),
);
+ this.shouldShowWarning$ = this.itemExceeds();
+ }
+
+ /**
+ * Checks if the export limit has been exceeded and updates the tooltip accordingly
+ */
+ private itemExceeds(): Observable {
+ return this.configurationService.findByPropertyName('metadataexport.max.items').pipe(
+ getFirstCompletedRemoteData(),
+ map((response: RemoteData) => {
+ const limit = Number(response.payload?.values?.[0]);
+ if (response.hasSucceeded && limit < this.total) {
+ this.exportLimitExceededMsg = this.translateService.instant(this.exportLimitExceededKey, { limit: response.payload?.values?.[0] });
+ return true;
+ } else {
+ return false;
+ }
+ }),
+ );
}
/**
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 871b2b2ca8..10ef1ea2cb 100644
--- a/src/app/shared/search/search-results/search-results.component.html
+++ b/src/app/shared/search/search-results/search-results.component.html
@@ -1,6 +1,7 @@
{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}
-
+
0" @fadeIn>
Date: Mon, 3 Feb 2025 10:11:28 +0100
Subject: [PATCH 04/54] 119612: Check if a warning should be shown on changes
to the total elements of the search, default to 500 if no value for the
configuration property was returned
---
.../search-export-csv.component.ts | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.ts
index 8b6252ada7..2727c6cce7 100644
--- a/src/app/shared/search/search-export-csv/search-export-csv.component.ts
+++ b/src/app/shared/search/search-export-csv/search-export-csv.component.ts
@@ -5,7 +5,9 @@ import {
import {
Component,
Input,
+ OnChanges,
OnInit,
+ SimpleChanges,
} from '@angular/core';
import { Router } from '@angular/router';
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
@@ -48,7 +50,7 @@ import { SearchFilter } from '../models/search-filter.model';
/**
* Display a button to export the current search results as csv
*/
-export class SearchExportCsvComponent implements OnInit {
+export class SearchExportCsvComponent implements OnInit, OnChanges {
/**
* The current configuration of the search
@@ -94,16 +96,22 @@ export class SearchExportCsvComponent implements OnInit {
this.shouldShowWarning$ = this.itemExceeds();
}
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.total) {
+ this.shouldShowWarning$ = this.itemExceeds();
+ }
+ }
+
/**
* Checks if the export limit has been exceeded and updates the tooltip accordingly
*/
private itemExceeds(): Observable {
- return this.configurationService.findByPropertyName('metadataexport.max.items').pipe(
+ return this.configurationService.findByPropertyName('bulkedit.export.max.items').pipe(
getFirstCompletedRemoteData(),
map((response: RemoteData) => {
- const limit = Number(response.payload?.values?.[0]);
- if (response.hasSucceeded && limit < this.total) {
- this.exportLimitExceededMsg = this.translateService.instant(this.exportLimitExceededKey, { limit: response.payload?.values?.[0] });
+ const limit = Number(response.payload?.values?.[0]) || 500;
+ if (limit < this.total) {
+ this.exportLimitExceededMsg = this.translateService.instant(this.exportLimitExceededKey, { limit: String(limit) });
return true;
} else {
return false;
From 8eaff7873761d8ef5c34af98780f28c16c374700 Mon Sep 17 00:00:00 2001
From: Jens Vannerum
Date: Mon, 3 Feb 2025 11:20:18 +0100
Subject: [PATCH 05/54] 119612: aria-label to also include warning message if
applicable
---
.../search/search-export-csv/search-export-csv.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.html b/src/app/shared/search/search-export-csv/search-export-csv.component.html
index 35ef89b728..8c92ff8fbd 100644
--- a/src/app/shared/search/search-export-csv/search-export-csv.component.html
+++ b/src/app/shared/search/search-export-csv/search-export-csv.component.html
@@ -15,6 +15,6 @@
class="export-button btn btn-dark btn-sm"
[ngbTooltip]="(shouldShowWarning$ | async) ? tipContentWarning : tipContent"
(click)="export()"
- [title]="tooltipMsg |translate" [attr.aria-label]="tooltipMsg |translate">
+ [title]="tooltipMsg | translate" [attr.aria-label]="(shouldShowWarning$ | async) ? ((tooltipMsg | translate) + ' ' + exportLimitExceededMsg): (tooltipMsg | translate)">
From 858ec87f2f115d5bc73cf9e4d260750c3d9eb2bf Mon Sep 17 00:00:00 2001
From: Kristof De Langhe
Date: Thu, 20 Feb 2025 14:14:03 +0100
Subject: [PATCH 06/54] 126853: Themeable SubmissionSectionContainerComponent
and SubmissionFormFooterComponent
---
...themed-submission-form-footer.component.ts | 27 +++++++++++++++++
.../form/submission-form.component.html | 8 ++---
.../themed-section-container.component.ts | 29 +++++++++++++++++++
src/app/submission/submission.module.ts | 4 +++
.../submission-form-footer.component.html | 0
.../submission-form-footer.component.scss | 0
.../submission-form-footer.component.ts | 15 ++++++++++
.../section-container.component.html | 0
.../section-container.component.scss | 0
.../container/section-container.component.ts | 15 ++++++++++
src/themes/custom/lazy-theme.module.ts | 4 +++
11 files changed, 98 insertions(+), 4 deletions(-)
create mode 100644 src/app/submission/form/footer/themed-submission-form-footer.component.ts
create mode 100644 src/app/submission/sections/container/themed-section-container.component.ts
create mode 100644 src/themes/custom/app/submission/form/footer/submission-form-footer.component.html
create mode 100644 src/themes/custom/app/submission/form/footer/submission-form-footer.component.scss
create mode 100644 src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
create mode 100644 src/themes/custom/app/submission/sections/container/section-container.component.html
create mode 100644 src/themes/custom/app/submission/sections/container/section-container.component.scss
create mode 100644 src/themes/custom/app/submission/sections/container/section-container.component.ts
diff --git a/src/app/submission/form/footer/themed-submission-form-footer.component.ts b/src/app/submission/form/footer/themed-submission-form-footer.component.ts
new file mode 100644
index 0000000000..041a090f32
--- /dev/null
+++ b/src/app/submission/form/footer/themed-submission-form-footer.component.ts
@@ -0,0 +1,27 @@
+import { ThemedComponent } from '../../../shared/theme-support/themed.component';
+import { SubmissionFormFooterComponent } from './submission-form-footer.component';
+import { Component, Input } from '@angular/core';
+
+@Component({
+ selector: 'ds-themed-submission-form-footer',
+ styleUrls: [],
+ templateUrl: '../../../shared/theme-support/themed.component.html',
+})
+export class ThemedSubmissionFormFooterComponent extends ThemedComponent {
+ @Input() submissionId: string;
+
+ protected inAndOutputNames: (keyof SubmissionFormFooterComponent & keyof this)[] = ['submissionId'];
+
+ protected getComponentName(): string {
+ return 'SubmissionFormFooterComponent';
+ }
+
+ protected importThemedComponent(themeName: string): Promise {
+ return import(`../../../../themes/${themeName}/app/submission/form/footer/submission-form-footer.component`);
+ }
+
+ protected importUnthemedComponent(): Promise {
+ return import(`./submission-form-footer.component`);
+ }
+
+}
diff --git a/src/app/submission/form/submission-form.component.html b/src/app/submission/form/submission-form.component.html
index 4a916cfe23..9ca8090564 100644
--- a/src/app/submission/form/submission-form.component.html
+++ b/src/app/submission/form/submission-form.component.html
@@ -28,12 +28,12 @@
-
+
diff --git a/src/app/submission/sections/container/themed-section-container.component.ts b/src/app/submission/sections/container/themed-section-container.component.ts
new file mode 100644
index 0000000000..11ec6b63d7
--- /dev/null
+++ b/src/app/submission/sections/container/themed-section-container.component.ts
@@ -0,0 +1,29 @@
+import { ThemedComponent } from '../../../shared/theme-support/themed.component';
+import { SubmissionSectionContainerComponent } from './section-container.component';
+import { Component, Input } from '@angular/core';
+import { SectionDataObject } from '../models/section-data.model';
+
+@Component({
+ selector: 'ds-themed-submission-section-container',
+ styleUrls: [],
+ templateUrl: '../../../shared/theme-support/themed.component.html',
+})
+export class ThemedSubmissionSectionContainerComponent extends ThemedComponent {
+ @Input() collectionId: string;
+ @Input() sectionData: SectionDataObject;
+ @Input() submissionId: string;
+
+ protected inAndOutputNames: (keyof SubmissionSectionContainerComponent & keyof this)[] = ['collectionId', 'sectionData', 'submissionId'];
+
+ protected getComponentName(): string {
+ return 'SubmissionSectionContainerComponent';
+ }
+
+ protected importThemedComponent(themeName: string): Promise {
+ return import(`../../../../themes/${themeName}/app/submission/sections/container/section-container.component`);
+ }
+
+ protected importUnthemedComponent(): Promise {
+ return import(`./section-container.component`);
+ }
+}
diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts
index cf0ab2b369..8807a76fe4 100644
--- a/src/app/submission/submission.module.ts
+++ b/src/app/submission/submission.module.ts
@@ -67,6 +67,8 @@ import {
} from './sections/sherpa-policies/metadata-information/metadata-information.component';
import { SectionFormOperationsService } from './sections/form/section-form-operations.service';
import {SubmissionSectionIdentifiersComponent} from './sections/identifiers/section-identifiers.component';
+import { ThemedSubmissionSectionContainerComponent } from './sections/container/themed-section-container.component';
+import { ThemedSubmissionFormFooterComponent } from './form/footer/themed-submission-form-footer.component';
const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
@@ -106,6 +108,8 @@ const DECLARATIONS = [
PublicationInformationComponent,
MetadataInformationComponent,
ThemedSubmissionSectionUploadFileComponent,
+ ThemedSubmissionSectionContainerComponent,
+ ThemedSubmissionFormFooterComponent,
];
@NgModule({
diff --git a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.html b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.scss b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
new file mode 100644
index 0000000000..3a93410edf
--- /dev/null
+++ b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
@@ -0,0 +1,15 @@
+import {
+ SubmissionFormFooterComponent as BaseComponent
+} from '../../../../../../app/submission/form/footer/submission-form-footer.component';
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ds-submission-form-footer',
+ // styleUrls: ['./submission-form-footer.component.scss'],
+ styleUrls: ['../../../../../../app/submission/form/footer/submission-form-footer.component.scss'],
+ // templateUrl: './submission-form-footer.component.html'
+ templateUrl: '../../../../../../app/submission/form/footer/submission-form-footer.component.html'
+})
+export class SubmissionFormFooterComponent extends BaseComponent {
+
+}
diff --git a/src/themes/custom/app/submission/sections/container/section-container.component.html b/src/themes/custom/app/submission/sections/container/section-container.component.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/themes/custom/app/submission/sections/container/section-container.component.scss b/src/themes/custom/app/submission/sections/container/section-container.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/themes/custom/app/submission/sections/container/section-container.component.ts b/src/themes/custom/app/submission/sections/container/section-container.component.ts
new file mode 100644
index 0000000000..fcb6855f78
--- /dev/null
+++ b/src/themes/custom/app/submission/sections/container/section-container.component.ts
@@ -0,0 +1,15 @@
+import {
+ SubmissionSectionContainerComponent as BaseComponent
+} from '../../../../../../app/submission/sections/container/section-container.component';
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ds-submission-upload-section-file',
+ // styleUrls: ['./section-container.component.scss'],
+ styleUrls: ['../../../../../../app/submission/sections/container/section-container.component.scss'],
+ // templateUrl: './section-container.component.html'
+ templateUrl: '../../../../../../app/submission/sections/container/section-container.component.html'
+})
+export class SubmissionSectionContainerComponent extends BaseComponent {
+
+}
diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts
index edb3f5478c..a52ba8f04a 100644
--- a/src/themes/custom/lazy-theme.module.ts
+++ b/src/themes/custom/lazy-theme.module.ts
@@ -156,6 +156,8 @@ import { ItemStatusComponent } from './app/item-page/edit-item-page/item-status/
import { EditBitstreamPageComponent } from './app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component';
import { FormModule } from '../../app/shared/form/form.module';
import { RequestCopyModule } from 'src/app/request-copy/request-copy.module';
+import { SubmissionSectionContainerComponent } from './app/submission/sections/container/section-container.component';
+import { SubmissionFormFooterComponent } from './app/submission/form/footer/submission-form-footer.component';
const DECLARATIONS = [
FileSectionComponent,
@@ -239,6 +241,8 @@ const DECLARATIONS = [
SubmissionSectionUploadFileComponent,
ItemStatusComponent,
EditBitstreamPageComponent,
+ SubmissionSectionContainerComponent,
+ SubmissionFormFooterComponent,
];
@NgModule({
From 261af2a2f53417e59063dc8a38007c14015e5333 Mon Sep 17 00:00:00 2001
From: Kristof De Langhe
Date: Mon, 3 Mar 2025 10:30:16 +0100
Subject: [PATCH 07/54] 126853: ThemedSubmissionFormComponent
---
.../edit/submission-edit.component.html | 4 +-
.../form/themed-submission-form.component.ts | 44 +++++++++++++++++++
src/app/submission/submission.module.ts | 2 +
.../form/submission-form.component.html | 0
.../form/submission-form.component.scss | 0
.../form/submission-form.component.ts | 15 +++++++
src/themes/custom/lazy-theme.module.ts | 2 +
7 files changed, 65 insertions(+), 2 deletions(-)
create mode 100644 src/app/submission/form/themed-submission-form.component.ts
create mode 100644 src/themes/custom/app/submission/form/submission-form.component.html
create mode 100644 src/themes/custom/app/submission/form/submission-form.component.scss
create mode 100644 src/themes/custom/app/submission/form/submission-form.component.ts
diff --git a/src/app/submission/edit/submission-edit.component.html b/src/app/submission/edit/submission-edit.component.html
index 9c0e9eae72..71702d8c7d 100644
--- a/src/app/submission/edit/submission-edit.component.html
+++ b/src/app/submission/edit/submission-edit.component.html
@@ -1,10 +1,10 @@
-
+ [submissionId]="submissionId">
diff --git a/src/app/submission/form/themed-submission-form.component.ts b/src/app/submission/form/themed-submission-form.component.ts
new file mode 100644
index 0000000000..96b5ecebd5
--- /dev/null
+++ b/src/app/submission/form/themed-submission-form.component.ts
@@ -0,0 +1,44 @@
+import { ThemedComponent } from '../../shared/theme-support/themed.component';
+import { SubmissionFormComponent } from './submission-form.component';
+import { Component, Input } from '@angular/core';
+import { Item } from '../../core/shared/item.model';
+import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model';
+import { SubmissionError } from '../objects/submission-error.model';
+import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model';
+
+@Component({
+ selector: 'ds-themed-submission-form',
+ styleUrls: [],
+ templateUrl: '../../shared/theme-support/themed.component.html',
+})
+export class ThemedSubmissionFormComponent extends ThemedComponent {
+ @Input() collectionId: string;
+
+ @Input() item: Item;
+
+ @Input() collectionModifiable: boolean | null = null;
+
+ @Input() sections: WorkspaceitemSectionsObject;
+
+ @Input() submissionErrors: SubmissionError;
+
+ @Input() selfUrl: string;
+
+ @Input() submissionDefinition: SubmissionDefinitionsModel;
+
+ @Input() submissionId: string;
+
+ protected inAndOutputNames: (keyof SubmissionFormComponent & keyof this)[] = ['collectionId', 'item', 'collectionModifiable', 'sections', 'submissionErrors', 'selfUrl', 'submissionDefinition', 'submissionId'];
+
+ protected getComponentName(): string {
+ return 'SubmissionFormComponent';
+ }
+
+ protected importThemedComponent(themeName: string): Promise {
+ return import(`../../../themes/${themeName}/app/submission/form/submission-form.component`);
+ }
+
+ protected importUnthemedComponent(): Promise {
+ return import(`./submission-form.component`);
+ }
+}
diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts
index 8807a76fe4..c710b3b297 100644
--- a/src/app/submission/submission.module.ts
+++ b/src/app/submission/submission.module.ts
@@ -69,6 +69,7 @@ import { SectionFormOperationsService } from './sections/form/section-form-opera
import {SubmissionSectionIdentifiersComponent} from './sections/identifiers/section-identifiers.component';
import { ThemedSubmissionSectionContainerComponent } from './sections/container/themed-section-container.component';
import { ThemedSubmissionFormFooterComponent } from './form/footer/themed-submission-form-footer.component';
+import { ThemedSubmissionFormComponent } from './form/themed-submission-form.component';
const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
@@ -110,6 +111,7 @@ const DECLARATIONS = [
ThemedSubmissionSectionUploadFileComponent,
ThemedSubmissionSectionContainerComponent,
ThemedSubmissionFormFooterComponent,
+ ThemedSubmissionFormComponent,
];
@NgModule({
diff --git a/src/themes/custom/app/submission/form/submission-form.component.html b/src/themes/custom/app/submission/form/submission-form.component.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/themes/custom/app/submission/form/submission-form.component.scss b/src/themes/custom/app/submission/form/submission-form.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/themes/custom/app/submission/form/submission-form.component.ts b/src/themes/custom/app/submission/form/submission-form.component.ts
new file mode 100644
index 0000000000..15b52a2657
--- /dev/null
+++ b/src/themes/custom/app/submission/form/submission-form.component.ts
@@ -0,0 +1,15 @@
+import {
+ SubmissionFormComponent as BaseComponent
+} from '../../../../../app/submission/form/submission-form.component';
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ds-submission-form',
+ // styleUrls: ['./submission-form.component.scss'],
+ styleUrls: ['../../../../../app/submission/form/submission-form.component.scss'],
+ // templateUrl: './submission-form.component.html'
+ templateUrl: '../../../../../app/submission/form/submission-form.component.html'
+})
+export class SubmissionFormComponent extends BaseComponent {
+
+}
diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts
index a52ba8f04a..de7aea3243 100644
--- a/src/themes/custom/lazy-theme.module.ts
+++ b/src/themes/custom/lazy-theme.module.ts
@@ -158,6 +158,7 @@ import { FormModule } from '../../app/shared/form/form.module';
import { RequestCopyModule } from 'src/app/request-copy/request-copy.module';
import { SubmissionSectionContainerComponent } from './app/submission/sections/container/section-container.component';
import { SubmissionFormFooterComponent } from './app/submission/form/footer/submission-form-footer.component';
+import { SubmissionFormComponent } from './app/submission/form/submission-form.component';
const DECLARATIONS = [
FileSectionComponent,
@@ -243,6 +244,7 @@ const DECLARATIONS = [
EditBitstreamPageComponent,
SubmissionSectionContainerComponent,
SubmissionFormFooterComponent,
+ SubmissionFormComponent,
];
@NgModule({
From c0bfc3a73982ecc45eff16e1b40b342b5433f826 Mon Sep 17 00:00:00 2001
From: Sascha Szott
Date: Thu, 6 Mar 2025 14:34:25 +0100
Subject: [PATCH 08/54] apply spellCheck configuration to input model
---
src/app/shared/form/builder/parsers/onebox-field-parser.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.ts
index 50fc585abf..2ae456a6ea 100644
--- a/src/app/shared/form/builder/parsers/onebox-field-parser.ts
+++ b/src/app/shared/form/builder/parsers/onebox-field-parser.ts
@@ -90,6 +90,7 @@ export class OneboxFieldParser extends FieldParser {
return new DynamicOneboxModel(oneboxModelConfig);
} else {
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(null, label);
+ inputModelConfig.spellCheck = environment.form.spellCheck;
this.setValues(inputModelConfig, fieldValue);
return new DsDynamicInputModel(inputModelConfig);
From c835b215af0cb79308b31a5b5dd6b89104ad1420 Mon Sep 17 00:00:00 2001
From: Sascha Szott
Date: Thu, 6 Mar 2025 14:43:41 +0100
Subject: [PATCH 09/54] add import
---
src/app/shared/form/builder/parsers/onebox-field-parser.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.ts
index 2ae456a6ea..e5a951359b 100644
--- a/src/app/shared/form/builder/parsers/onebox-field-parser.ts
+++ b/src/app/shared/form/builder/parsers/onebox-field-parser.ts
@@ -21,6 +21,7 @@ import {
} from '../ds-dynamic-form-ui/models/onebox/dynamic-onebox.model';
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
import { FieldParser } from './field-parser';
+import { environment } from '../../../../../environments/environment';
export class OneboxFieldParser extends FieldParser {
From 13ae42f217fa7f32b7a96995322538b2aaf12c84 Mon Sep 17 00:00:00 2001
From: Sascha Szott
Date: Thu, 6 Mar 2025 14:59:41 +0100
Subject: [PATCH 10/54] reorganize imports
---
src/app/shared/form/builder/parsers/onebox-field-parser.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.ts
index e5a951359b..23c5d73efb 100644
--- a/src/app/shared/form/builder/parsers/onebox-field-parser.ts
+++ b/src/app/shared/form/builder/parsers/onebox-field-parser.ts
@@ -3,6 +3,7 @@ import {
DynamicSelectModelConfig,
} from '@ng-dynamic-forms/core';
+import { environment } from '../../../../../environments/environment';
import { isNotEmpty } from '../../../empty.util';
import {
DsDynamicInputModel,
@@ -21,7 +22,6 @@ import {
} from '../ds-dynamic-form-ui/models/onebox/dynamic-onebox.model';
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
import { FieldParser } from './field-parser';
-import { environment } from '../../../../../environments/environment';
export class OneboxFieldParser extends FieldParser {
From da2c98aa3224b2a2af48530ab142196e7aa8b874 Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Fri, 14 Mar 2025 13:45:43 +0200
Subject: [PATCH 11/54] Fix lint issues
---
.../edit/submission-edit.component.html | 4 +-
.../edit/submission-edit.component.ts | 4 +-
.../submission-form-footer.component.ts | 2 +-
...themed-submission-form-footer.component.ts | 10 +-
.../form/submission-form.component.html | 8 +-
.../form/submission-form.component.ts | 10 +-
.../form/themed-submission-form.component.ts | 16 ++-
.../container/section-container.component.ts | 2 +-
.../themed-section-container.component.ts | 12 +-
src/app/submission/submission.module.ts | 130 ++++++++----------
.../edit/submission-edit.component.ts | 4 +-
.../submission-form-footer.component.ts | 10 +-
.../form/submission-form.component.ts | 10 +-
.../container/section-container.component.ts | 10 +-
src/themes/custom/lazy-theme.module.ts | 6 +-
15 files changed, 119 insertions(+), 119 deletions(-)
diff --git a/src/app/submission/edit/submission-edit.component.html b/src/app/submission/edit/submission-edit.component.html
index 71702d8c7d..9c0e9eae72 100644
--- a/src/app/submission/edit/submission-edit.component.html
+++ b/src/app/submission/edit/submission-edit.component.html
@@ -1,10 +1,10 @@
-
+ [submissionId]="submissionId">
diff --git a/src/app/submission/edit/submission-edit.component.ts b/src/app/submission/edit/submission-edit.component.ts
index 822818ee24..c2a914c85d 100644
--- a/src/app/submission/edit/submission-edit.component.ts
+++ b/src/app/submission/edit/submission-edit.component.ts
@@ -36,7 +36,7 @@ import {
isNotNull,
} from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
-import { SubmissionFormComponent } from '../form/submission-form.component';
+import { ThemedSubmissionFormComponent } from '../form/themed-submission-form.component';
import { SubmissionError } from '../objects/submission-error.model';
import { SubmissionService } from '../submission.service';
import parseSectionErrors from '../utils/parseSectionErrors';
@@ -50,7 +50,7 @@ import parseSectionErrors from '../utils/parseSectionErrors';
templateUrl: './submission-edit.component.html',
standalone: true,
imports: [
- SubmissionFormComponent,
+ ThemedSubmissionFormComponent,
],
})
export class SubmissionEditComponent implements OnDestroy, OnInit {
diff --git a/src/app/submission/form/footer/submission-form-footer.component.ts b/src/app/submission/form/footer/submission-form-footer.component.ts
index 8645003783..a61e2599a2 100644
--- a/src/app/submission/form/footer/submission-form-footer.component.ts
+++ b/src/app/submission/form/footer/submission-form-footer.component.ts
@@ -24,7 +24,7 @@ import { SubmissionService } from '../../submission.service';
* This component represents submission form footer bar.
*/
@Component({
- selector: 'ds-submission-form-footer',
+ selector: 'ds-base-submission-form-footer',
styleUrls: ['./submission-form-footer.component.scss'],
templateUrl: './submission-form-footer.component.html',
standalone: true,
diff --git a/src/app/submission/form/footer/themed-submission-form-footer.component.ts b/src/app/submission/form/footer/themed-submission-form-footer.component.ts
index 041a090f32..82240abcf5 100644
--- a/src/app/submission/form/footer/themed-submission-form-footer.component.ts
+++ b/src/app/submission/form/footer/themed-submission-form-footer.component.ts
@@ -1,11 +1,17 @@
+import {
+ Component,
+ Input,
+} from '@angular/core';
+
import { ThemedComponent } from '../../../shared/theme-support/themed.component';
import { SubmissionFormFooterComponent } from './submission-form-footer.component';
-import { Component, Input } from '@angular/core';
@Component({
- selector: 'ds-themed-submission-form-footer',
+ selector: 'ds-submission-form-footer',
styleUrls: [],
templateUrl: '../../../shared/theme-support/themed.component.html',
+ standalone: true,
+ imports: [SubmissionFormFooterComponent],
})
export class ThemedSubmissionFormFooterComponent extends ThemedComponent {
@Input() submissionId: string;
diff --git a/src/app/submission/form/submission-form.component.html b/src/app/submission/form/submission-form.component.html
index fc424ed60d..802d0e1c4c 100644
--- a/src/app/submission/form/submission-form.component.html
+++ b/src/app/submission/form/submission-form.component.html
@@ -31,18 +31,18 @@
@if ((isLoading() | async)) {
-
+
}
@for (object of $any(submissionSections | async); track object) {
-
-
+
}
@if ((isLoading() | async) !== true) {
}
diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts
index 74c262befc..7a1d67f871 100644
--- a/src/app/submission/form/submission-form.component.ts
+++ b/src/app/submission/form/submission-form.component.ts
@@ -38,14 +38,14 @@ import { UploaderOptions } from '../../shared/upload/uploader/uploader-options.m
import { SectionVisibility } from '../objects/section-visibility.model';
import { SubmissionError } from '../objects/submission-error.model';
import { SubmissionObjectEntry } from '../objects/submission-objects.reducer';
-import { SubmissionSectionContainerComponent } from '../sections/container/section-container.component';
+import { ThemedSubmissionSectionContainerComponent } from '../sections/container/themed-section-container.component';
import { SectionDataObject } from '../sections/models/section-data.model';
import { SectionsService } from '../sections/sections.service';
import { SectionsType } from '../sections/sections-type';
import { VisibilityType } from '../sections/visibility-type';
import { SubmissionService } from '../submission.service';
import { SubmissionFormCollectionComponent } from './collection/submission-form-collection.component';
-import { SubmissionFormFooterComponent } from './footer/submission-form-footer.component';
+import { ThemedSubmissionFormFooterComponent } from './footer/themed-submission-form-footer.component';
import { SubmissionFormSectionAddComponent } from './section-add/submission-form-section-add.component';
import { ThemedSubmissionUploadFilesComponent } from './submission-upload-files/themed-submission-upload-files.component';
@@ -53,14 +53,14 @@ import { ThemedSubmissionUploadFilesComponent } from './submission-upload-files/
* This component represents the submission form.
*/
@Component({
- selector: 'ds-submission-form',
+ selector: 'ds-base-submission-form',
styleUrls: ['./submission-form.component.scss'],
templateUrl: './submission-form.component.html',
imports: [
CommonModule,
ThemedLoadingComponent,
- SubmissionSectionContainerComponent,
- SubmissionFormFooterComponent,
+ ThemedSubmissionSectionContainerComponent,
+ ThemedSubmissionFormFooterComponent,
ThemedSubmissionUploadFilesComponent,
SubmissionFormCollectionComponent,
SubmissionFormSectionAddComponent,
diff --git a/src/app/submission/form/themed-submission-form.component.ts b/src/app/submission/form/themed-submission-form.component.ts
index 96b5ecebd5..b7414aa8e9 100644
--- a/src/app/submission/form/themed-submission-form.component.ts
+++ b/src/app/submission/form/themed-submission-form.component.ts
@@ -1,15 +1,21 @@
-import { ThemedComponent } from '../../shared/theme-support/themed.component';
-import { SubmissionFormComponent } from './submission-form.component';
-import { Component, Input } from '@angular/core';
+import {
+ Component,
+ Input,
+} from '@angular/core';
+
+import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model';
import { Item } from '../../core/shared/item.model';
import { WorkspaceitemSectionsObject } from '../../core/submission/models/workspaceitem-sections.model';
+import { ThemedComponent } from '../../shared/theme-support/themed.component';
import { SubmissionError } from '../objects/submission-error.model';
-import { SubmissionDefinitionsModel } from '../../core/config/models/config-submission-definitions.model';
+import { SubmissionFormComponent } from './submission-form.component';
@Component({
- selector: 'ds-themed-submission-form',
+ selector: 'ds-submission-form',
styleUrls: [],
templateUrl: '../../shared/theme-support/themed.component.html',
+ standalone: true,
+ imports: [SubmissionFormComponent],
})
export class ThemedSubmissionFormComponent extends ThemedComponent {
@Input() collectionId: string;
diff --git a/src/app/submission/sections/container/section-container.component.ts b/src/app/submission/sections/container/section-container.component.ts
index 96f8cbcff6..174aed93b5 100644
--- a/src/app/submission/sections/container/section-container.component.ts
+++ b/src/app/submission/sections/container/section-container.component.ts
@@ -23,7 +23,7 @@ import { rendersSectionType } from '../sections-decorator';
* This component represents a section that contains the submission license form.
*/
@Component({
- selector: 'ds-submission-section-container',
+ selector: 'ds-base-submission-section-container',
templateUrl: './section-container.component.html',
styleUrls: ['./section-container.component.scss'],
imports: [
diff --git a/src/app/submission/sections/container/themed-section-container.component.ts b/src/app/submission/sections/container/themed-section-container.component.ts
index 11ec6b63d7..951fb2bdcb 100644
--- a/src/app/submission/sections/container/themed-section-container.component.ts
+++ b/src/app/submission/sections/container/themed-section-container.component.ts
@@ -1,12 +1,18 @@
+import {
+ Component,
+ Input,
+} from '@angular/core';
+
import { ThemedComponent } from '../../../shared/theme-support/themed.component';
-import { SubmissionSectionContainerComponent } from './section-container.component';
-import { Component, Input } from '@angular/core';
import { SectionDataObject } from '../models/section-data.model';
+import { SubmissionSectionContainerComponent } from './section-container.component';
@Component({
- selector: 'ds-themed-submission-section-container',
+ selector: 'ds-submission-section-container',
styleUrls: [],
templateUrl: '../../../shared/theme-support/themed.component.html',
+ standalone: true,
+ imports: [SubmissionSectionContainerComponent],
})
export class ThemedSubmissionSectionContainerComponent extends ThemedComponent {
@Input() collectionId: string;
diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts
index c710b3b297..59305dd60e 100644
--- a/src/app/submission/submission.module.ts
+++ b/src/app/submission/submission.module.ts
@@ -1,75 +1,64 @@
-import { NgModule } from '@angular/core';
-import { CoreModule } from '../core/core.module';
-import { SharedModule } from '../shared/shared.module';
-
-import { SubmissionSectionFormComponent } from './sections/form/section-form.component';
-import { SectionsDirective } from './sections/sections.directive';
-import { SectionsService } from './sections/sections.service';
-import { SubmissionFormCollectionComponent } from './form/collection/submission-form-collection.component';
-import { SubmissionFormFooterComponent } from './form/footer/submission-form-footer.component';
-import { SubmissionFormComponent } from './form/submission-form.component';
-import { SubmissionFormSectionAddComponent } from './form/section-add/submission-form-section-add.component';
-import { SubmissionSectionContainerComponent } from './sections/container/section-container.component';
import { CommonModule } from '@angular/common';
-import { Action, StoreConfig, StoreModule } from '@ngrx/store';
+import { NgModule } from '@angular/core';
+import {
+ NgbAccordionModule,
+ NgbCollapseModule,
+ NgbModalModule,
+} from '@ng-bootstrap/ng-bootstrap';
import { EffectsModule } from '@ngrx/effects';
-import { submissionReducers, SubmissionState } from './submission.reducers';
-import { submissionEffects } from './submission.effects';
-import { SubmissionSectionUploadComponent } from './sections/upload/section-upload.component';
-import { SectionUploadService } from './sections/upload/section-upload.service';
-import { SubmissionUploadFilesComponent } from './form/submission-upload-files/submission-upload-files.component';
-import { SubmissionSectionLicenseComponent } from './sections/license/section-license.component';
+import {
+ Action,
+ StoreConfig,
+ StoreModule,
+} from '@ngrx/store';
+
+import { storeModuleConfig } from '../app.reducer';
+import { SubmissionAccessesConfigDataService } from '../core/config/submission-accesses-config-data.service';
import { SubmissionUploadsConfigDataService } from '../core/config/submission-uploads-config-data.service';
import { SubmissionEditComponent } from './edit/submission-edit.component';
-import { SubmissionSectionUploadFileComponent } from './sections/upload/file/section-upload-file.component';
-import {
- SubmissionSectionUploadFileEditComponent
-} from './sections/upload/file/edit/section-upload-file-edit.component';
-import {
- SubmissionSectionUploadFileViewComponent
-} from './sections/upload/file/view/section-upload-file-view.component';
-import {
- SubmissionSectionUploadAccessConditionsComponent
-} from './sections/upload/accessConditions/submission-section-upload-access-conditions.component';
-import { SubmissionSubmitComponent } from './submit/submission-submit.component';
-import { storeModuleConfig } from '../app.reducer';
-import { SubmissionImportExternalComponent } from './import-external/submission-import-external.component';
-import {
- SubmissionImportExternalSearchbarComponent
-} from './import-external/import-external-searchbar/submission-import-external-searchbar.component';
-import {
- SubmissionImportExternalPreviewComponent
-} from './import-external/import-external-preview/submission-import-external-preview.component';
-import {
- SubmissionImportExternalCollectionComponent
-} from './import-external/import-external-collection/submission-import-external-collection.component';
-import { SubmissionSectionCcLicensesComponent } from './sections/cc-license/submission-section-cc-licenses.component';
-import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module';
-import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module';
import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component';
-import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component';
-import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component';
-import { ThemedSubmissionSectionUploadFileComponent } from './sections/upload/file/themed-section-upload-file.component';
-import { FormModule } from '../shared/form/form.module';
-import { NgbAccordionModule, NgbCollapseModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
-import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component';
-import { SubmissionAccessesConfigDataService } from '../core/config/submission-accesses-config-data.service';
-import { SectionAccessesService } from './sections/accesses/section-accesses.service';
-import { SubmissionSectionSherpaPoliciesComponent } from './sections/sherpa-policies/section-sherpa-policies.component';
-import { ContentAccordionComponent } from './sections/sherpa-policies/content-accordion/content-accordion.component';
-import { PublisherPolicyComponent } from './sections/sherpa-policies/publisher-policy/publisher-policy.component';
-import {
- PublicationInformationComponent
-} from './sections/sherpa-policies/publication-information/publication-information.component';
-import { UploadModule } from '../shared/upload/upload.module';
-import {
- MetadataInformationComponent
-} from './sections/sherpa-policies/metadata-information/metadata-information.component';
-import { SectionFormOperationsService } from './sections/form/section-form-operations.service';
-import {SubmissionSectionIdentifiersComponent} from './sections/identifiers/section-identifiers.component';
-import { ThemedSubmissionSectionContainerComponent } from './sections/container/themed-section-container.component';
+import { SubmissionFormCollectionComponent } from './form/collection/submission-form-collection.component';
+import { SubmissionFormFooterComponent } from './form/footer/submission-form-footer.component';
import { ThemedSubmissionFormFooterComponent } from './form/footer/themed-submission-form-footer.component';
+import { SubmissionFormSectionAddComponent } from './form/section-add/submission-form-section-add.component';
+import { SubmissionFormComponent } from './form/submission-form.component';
+import { SubmissionUploadFilesComponent } from './form/submission-upload-files/submission-upload-files.component';
import { ThemedSubmissionFormComponent } from './form/themed-submission-form.component';
+import { SubmissionImportExternalCollectionComponent } from './import-external/import-external-collection/submission-import-external-collection.component';
+import { SubmissionImportExternalPreviewComponent } from './import-external/import-external-preview/submission-import-external-preview.component';
+import { SubmissionImportExternalSearchbarComponent } from './import-external/import-external-searchbar/submission-import-external-searchbar.component';
+import { SubmissionImportExternalComponent } from './import-external/submission-import-external.component';
+import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component';
+import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component';
+import { SectionAccessesService } from './sections/accesses/section-accesses.service';
+import { SubmissionSectionCcLicensesComponent } from './sections/cc-license/submission-section-cc-licenses.component';
+import { SubmissionSectionContainerComponent } from './sections/container/section-container.component';
+import { ThemedSubmissionSectionContainerComponent } from './sections/container/themed-section-container.component';
+import { SubmissionSectionFormComponent } from './sections/form/section-form.component';
+import { SectionFormOperationsService } from './sections/form/section-form-operations.service';
+import { SubmissionSectionIdentifiersComponent } from './sections/identifiers/section-identifiers.component';
+import { SubmissionSectionLicenseComponent } from './sections/license/section-license.component';
+import { SectionsDirective } from './sections/sections.directive';
+import { SectionsService } from './sections/sections.service';
+import { ContentAccordionComponent } from './sections/sherpa-policies/content-accordion/content-accordion.component';
+import { MetadataInformationComponent } from './sections/sherpa-policies/metadata-information/metadata-information.component';
+import { PublicationInformationComponent } from './sections/sherpa-policies/publication-information/publication-information.component';
+import { PublisherPolicyComponent } from './sections/sherpa-policies/publisher-policy/publisher-policy.component';
+import { SubmissionSectionSherpaPoliciesComponent } from './sections/sherpa-policies/section-sherpa-policies.component';
+import { SubmissionSectionUploadAccessConditionsComponent } from './sections/upload/accessConditions/submission-section-upload-access-conditions.component';
+import { SubmissionSectionUploadFileEditComponent } from './sections/upload/file/edit/section-upload-file-edit.component';
+import { SubmissionSectionUploadFileComponent } from './sections/upload/file/section-upload-file.component';
+import { ThemedSubmissionSectionUploadFileComponent } from './sections/upload/file/themed-section-upload-file.component';
+import { SubmissionSectionUploadFileViewComponent } from './sections/upload/file/view/section-upload-file-view.component';
+import { SubmissionSectionUploadComponent } from './sections/upload/section-upload.component';
+import { SectionUploadService } from './sections/upload/section-upload.service';
+import { submissionEffects } from './submission.effects';
+import {
+ submissionReducers,
+ SubmissionState,
+} from './submission.reducers';
+import { SubmissionSubmitComponent } from './submit/submission-submit.component';
+import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component';
const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
@@ -117,22 +106,15 @@ const DECLARATIONS = [
@NgModule({
imports: [
CommonModule,
- CoreModule.forRoot(),
- SharedModule,
StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig),
EffectsModule.forFeature(submissionEffects),
- JournalEntitiesModule.withEntryComponents(),
- ResearchEntitiesModule.withEntryComponents(),
- FormModule,
NgbModalModule,
NgbCollapseModule,
NgbAccordionModule,
- UploadModule,
],
declarations: DECLARATIONS,
exports: [
...DECLARATIONS,
- FormModule,
],
providers: [
SectionUploadService,
@@ -141,7 +123,7 @@ const DECLARATIONS = [
SubmissionAccessesConfigDataService,
SectionAccessesService,
SectionFormOperationsService,
- ]
+ ],
})
/**
@@ -155,7 +137,7 @@ export class SubmissionModule {
static withEntryComponents() {
return {
ngModule: SubmissionModule,
- providers: ENTRY_COMPONENTS.map((component) => ({ provide: component }))
+ providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })),
};
}
}
diff --git a/src/themes/custom/app/submission/edit/submission-edit.component.ts b/src/themes/custom/app/submission/edit/submission-edit.component.ts
index c41b4321fa..1afe61b44a 100644
--- a/src/themes/custom/app/submission/edit/submission-edit.component.ts
+++ b/src/themes/custom/app/submission/edit/submission-edit.component.ts
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { SubmissionEditComponent as BaseComponent } from '../../../../../app/submission/edit/submission-edit.component';
-import { SubmissionFormComponent } from '../../../../../app/submission/form/submission-form.component';
+import { ThemedSubmissionFormComponent } from '../../../../../app/submission/form/themed-submission-form.component';
/**
* This component allows to edit an existing workspaceitem/workflowitem.
@@ -14,7 +14,7 @@ import { SubmissionFormComponent } from '../../../../../app/submission/form/subm
templateUrl: '../../../../../app/submission/edit/submission-edit.component.html',
standalone: true,
imports: [
- SubmissionFormComponent,
+ ThemedSubmissionFormComponent,
],
})
export class SubmissionEditComponent extends BaseComponent {
diff --git a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
index 3a93410edf..c1deceecfa 100644
--- a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
+++ b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
@@ -1,14 +1,14 @@
-import {
- SubmissionFormFooterComponent as BaseComponent
-} from '../../../../../../app/submission/form/footer/submission-form-footer.component';
import { Component } from '@angular/core';
+import { SubmissionFormFooterComponent as BaseComponent } from '../../../../../../app/submission/form/footer/submission-form-footer.component';
+
@Component({
- selector: 'ds-submission-form-footer',
+ selector: 'ds-themed-submission-form-footer',
// styleUrls: ['./submission-form-footer.component.scss'],
styleUrls: ['../../../../../../app/submission/form/footer/submission-form-footer.component.scss'],
// templateUrl: './submission-form-footer.component.html'
- templateUrl: '../../../../../../app/submission/form/footer/submission-form-footer.component.html'
+ templateUrl: '../../../../../../app/submission/form/footer/submission-form-footer.component.html',
+ standalone: true,
})
export class SubmissionFormFooterComponent extends BaseComponent {
diff --git a/src/themes/custom/app/submission/form/submission-form.component.ts b/src/themes/custom/app/submission/form/submission-form.component.ts
index 15b52a2657..1c2cf07d09 100644
--- a/src/themes/custom/app/submission/form/submission-form.component.ts
+++ b/src/themes/custom/app/submission/form/submission-form.component.ts
@@ -1,14 +1,14 @@
-import {
- SubmissionFormComponent as BaseComponent
-} from '../../../../../app/submission/form/submission-form.component';
import { Component } from '@angular/core';
+import { SubmissionFormComponent as BaseComponent } from '../../../../../app/submission/form/submission-form.component';
+
@Component({
- selector: 'ds-submission-form',
+ selector: 'ds-themed-submission-form',
// styleUrls: ['./submission-form.component.scss'],
styleUrls: ['../../../../../app/submission/form/submission-form.component.scss'],
// templateUrl: './submission-form.component.html'
- templateUrl: '../../../../../app/submission/form/submission-form.component.html'
+ templateUrl: '../../../../../app/submission/form/submission-form.component.html',
+ standalone: true,
})
export class SubmissionFormComponent extends BaseComponent {
diff --git a/src/themes/custom/app/submission/sections/container/section-container.component.ts b/src/themes/custom/app/submission/sections/container/section-container.component.ts
index fcb6855f78..f24d679fb9 100644
--- a/src/themes/custom/app/submission/sections/container/section-container.component.ts
+++ b/src/themes/custom/app/submission/sections/container/section-container.component.ts
@@ -1,14 +1,14 @@
-import {
- SubmissionSectionContainerComponent as BaseComponent
-} from '../../../../../../app/submission/sections/container/section-container.component';
import { Component } from '@angular/core';
+import { SubmissionSectionContainerComponent as BaseComponent } from '../../../../../../app/submission/sections/container/section-container.component';
+
@Component({
- selector: 'ds-submission-upload-section-file',
+ selector: 'ds-themed-submission-upload-section-file',
// styleUrls: ['./section-container.component.scss'],
styleUrls: ['../../../../../../app/submission/sections/container/section-container.component.scss'],
// templateUrl: './section-container.component.html'
- templateUrl: '../../../../../../app/submission/sections/container/section-container.component.html'
+ templateUrl: '../../../../../../app/submission/sections/container/section-container.component.html',
+ standalone: true,
})
export class SubmissionSectionContainerComponent extends BaseComponent {
diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts
index a3f4e21d07..bc8e9ed07c 100644
--- a/src/themes/custom/lazy-theme.module.ts
+++ b/src/themes/custom/lazy-theme.module.ts
@@ -100,17 +100,17 @@ import { CommunityStatisticsPageComponent } from './app/statistics-page/communit
import { ItemStatisticsPageComponent } from './app/statistics-page/item-statistics-page/item-statistics-page.component';
import { SiteStatisticsPageComponent } from './app/statistics-page/site-statistics-page/site-statistics-page.component';
import { SubmissionEditComponent } from './app/submission/edit/submission-edit.component';
+import { SubmissionFormFooterComponent } from './app/submission/form/footer/submission-form-footer.component';
+import { SubmissionFormComponent } from './app/submission/form/submission-form.component';
import { SubmissionUploadFilesComponent } from './app/submission/form/submission-upload-files/submission-upload-files.component';
import { SubmissionImportExternalComponent } from './app/submission/import-external/submission-import-external.component';
+import { SubmissionSectionContainerComponent } from './app/submission/sections/container/section-container.component';
import { SubmissionSectionUploadFileComponent } from './app/submission/sections/upload/file/section-upload-file.component';
import { SubmissionSubmitComponent } from './app/submission/submit/submission-submit.component';
import { ThumbnailComponent } from './app/thumbnail/thumbnail.component';
import { WorkflowItemDeleteComponent } from './app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component';
import { WorkflowItemSendBackComponent } from './app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component';
import { WorkspaceItemsDeletePageComponent } from './app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component';
-import { SubmissionSectionContainerComponent } from './app/submission/sections/container/section-container.component';
-import { SubmissionFormFooterComponent } from './app/submission/form/footer/submission-form-footer.component';
-import { SubmissionFormComponent } from './app/submission/form/submission-form.component';
const DECLARATIONS = [
FileSectionComponent,
From fa8ffaefd0d0a2a66a12231ab2ae71efbe9afd7c Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Fri, 28 Mar 2025 15:08:12 +0200
Subject: [PATCH 12/54] 127047: Fix imports
---
src/app/submission/submission.module.ts | 143 ------------------
.../submission-form-footer.component.ts | 5 +
.../form/submission-form.component.ts | 16 ++
.../container/section-container.component.ts | 18 +++
4 files changed, 39 insertions(+), 143 deletions(-)
delete mode 100644 src/app/submission/submission.module.ts
diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts
deleted file mode 100644
index 59305dd60e..0000000000
--- a/src/app/submission/submission.module.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { NgModule } from '@angular/core';
-import {
- NgbAccordionModule,
- NgbCollapseModule,
- NgbModalModule,
-} from '@ng-bootstrap/ng-bootstrap';
-import { EffectsModule } from '@ngrx/effects';
-import {
- Action,
- StoreConfig,
- StoreModule,
-} from '@ngrx/store';
-
-import { storeModuleConfig } from '../app.reducer';
-import { SubmissionAccessesConfigDataService } from '../core/config/submission-accesses-config-data.service';
-import { SubmissionUploadsConfigDataService } from '../core/config/submission-uploads-config-data.service';
-import { SubmissionEditComponent } from './edit/submission-edit.component';
-import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component';
-import { SubmissionFormCollectionComponent } from './form/collection/submission-form-collection.component';
-import { SubmissionFormFooterComponent } from './form/footer/submission-form-footer.component';
-import { ThemedSubmissionFormFooterComponent } from './form/footer/themed-submission-form-footer.component';
-import { SubmissionFormSectionAddComponent } from './form/section-add/submission-form-section-add.component';
-import { SubmissionFormComponent } from './form/submission-form.component';
-import { SubmissionUploadFilesComponent } from './form/submission-upload-files/submission-upload-files.component';
-import { ThemedSubmissionFormComponent } from './form/themed-submission-form.component';
-import { SubmissionImportExternalCollectionComponent } from './import-external/import-external-collection/submission-import-external-collection.component';
-import { SubmissionImportExternalPreviewComponent } from './import-external/import-external-preview/submission-import-external-preview.component';
-import { SubmissionImportExternalSearchbarComponent } from './import-external/import-external-searchbar/submission-import-external-searchbar.component';
-import { SubmissionImportExternalComponent } from './import-external/submission-import-external.component';
-import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component';
-import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component';
-import { SectionAccessesService } from './sections/accesses/section-accesses.service';
-import { SubmissionSectionCcLicensesComponent } from './sections/cc-license/submission-section-cc-licenses.component';
-import { SubmissionSectionContainerComponent } from './sections/container/section-container.component';
-import { ThemedSubmissionSectionContainerComponent } from './sections/container/themed-section-container.component';
-import { SubmissionSectionFormComponent } from './sections/form/section-form.component';
-import { SectionFormOperationsService } from './sections/form/section-form-operations.service';
-import { SubmissionSectionIdentifiersComponent } from './sections/identifiers/section-identifiers.component';
-import { SubmissionSectionLicenseComponent } from './sections/license/section-license.component';
-import { SectionsDirective } from './sections/sections.directive';
-import { SectionsService } from './sections/sections.service';
-import { ContentAccordionComponent } from './sections/sherpa-policies/content-accordion/content-accordion.component';
-import { MetadataInformationComponent } from './sections/sherpa-policies/metadata-information/metadata-information.component';
-import { PublicationInformationComponent } from './sections/sherpa-policies/publication-information/publication-information.component';
-import { PublisherPolicyComponent } from './sections/sherpa-policies/publisher-policy/publisher-policy.component';
-import { SubmissionSectionSherpaPoliciesComponent } from './sections/sherpa-policies/section-sherpa-policies.component';
-import { SubmissionSectionUploadAccessConditionsComponent } from './sections/upload/accessConditions/submission-section-upload-access-conditions.component';
-import { SubmissionSectionUploadFileEditComponent } from './sections/upload/file/edit/section-upload-file-edit.component';
-import { SubmissionSectionUploadFileComponent } from './sections/upload/file/section-upload-file.component';
-import { ThemedSubmissionSectionUploadFileComponent } from './sections/upload/file/themed-section-upload-file.component';
-import { SubmissionSectionUploadFileViewComponent } from './sections/upload/file/view/section-upload-file-view.component';
-import { SubmissionSectionUploadComponent } from './sections/upload/section-upload.component';
-import { SectionUploadService } from './sections/upload/section-upload.service';
-import { submissionEffects } from './submission.effects';
-import {
- submissionReducers,
- SubmissionState,
-} from './submission.reducers';
-import { SubmissionSubmitComponent } from './submit/submission-submit.component';
-import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component';
-
-const ENTRY_COMPONENTS = [
- // put only entry components that use custom decorator
- SubmissionSectionUploadComponent,
- SubmissionSectionFormComponent,
- SubmissionSectionLicenseComponent,
- SubmissionSectionCcLicensesComponent,
- SubmissionSectionAccessesComponent,
- SubmissionSectionSherpaPoliciesComponent,
-];
-
-const DECLARATIONS = [
- ...ENTRY_COMPONENTS,
- SectionsDirective,
- SubmissionEditComponent,
- ThemedSubmissionEditComponent,
- SubmissionFormSectionAddComponent,
- SubmissionFormCollectionComponent,
- SubmissionFormComponent,
- SubmissionFormFooterComponent,
- SubmissionSubmitComponent,
- ThemedSubmissionSubmitComponent,
- SubmissionUploadFilesComponent,
- SubmissionSectionContainerComponent,
- SubmissionSectionUploadAccessConditionsComponent,
- SubmissionSectionUploadFileComponent,
- SubmissionSectionUploadFileEditComponent,
- SubmissionSectionUploadFileViewComponent,
- SubmissionSectionIdentifiersComponent,
- SubmissionImportExternalComponent,
- ThemedSubmissionImportExternalComponent,
- SubmissionImportExternalSearchbarComponent,
- SubmissionImportExternalPreviewComponent,
- SubmissionImportExternalCollectionComponent,
- ContentAccordionComponent,
- PublisherPolicyComponent,
- PublicationInformationComponent,
- MetadataInformationComponent,
- ThemedSubmissionSectionUploadFileComponent,
- ThemedSubmissionSectionContainerComponent,
- ThemedSubmissionFormFooterComponent,
- ThemedSubmissionFormComponent,
-];
-
-@NgModule({
- imports: [
- CommonModule,
- StoreModule.forFeature('submission', submissionReducers, storeModuleConfig as StoreConfig),
- EffectsModule.forFeature(submissionEffects),
- NgbModalModule,
- NgbCollapseModule,
- NgbAccordionModule,
- ],
- declarations: DECLARATIONS,
- exports: [
- ...DECLARATIONS,
- ],
- providers: [
- SectionUploadService,
- SectionsService,
- SubmissionUploadsConfigDataService,
- SubmissionAccessesConfigDataService,
- SectionAccessesService,
- SectionFormOperationsService,
- ],
-})
-
-/**
- * This module handles all components that are necessary for the submission process
- */
-export class SubmissionModule {
- /**
- * NOTE: this method allows to resolve issue with components that using a custom decorator
- * which are not loaded during SSR otherwise
- */
- static withEntryComponents() {
- return {
- ngModule: SubmissionModule,
- providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })),
- };
- }
-}
diff --git a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
index c1deceecfa..350a6204f3 100644
--- a/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
+++ b/src/themes/custom/app/submission/form/footer/submission-form-footer.component.ts
@@ -1,5 +1,9 @@
+import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
+import { TranslateModule } from '@ngx-translate/core';
+import { BtnDisabledDirective } from '../../../../../../app/shared/btn-disabled.directive';
+import { BrowserOnlyPipe } from '../../../../../../app/shared/utils/browser-only.pipe';
import { SubmissionFormFooterComponent as BaseComponent } from '../../../../../../app/submission/form/footer/submission-form-footer.component';
@Component({
@@ -9,6 +13,7 @@ import { SubmissionFormFooterComponent as BaseComponent } from '../../../../../.
// templateUrl: './submission-form-footer.component.html'
templateUrl: '../../../../../../app/submission/form/footer/submission-form-footer.component.html',
standalone: true,
+ imports: [CommonModule, BrowserOnlyPipe, TranslateModule, BtnDisabledDirective],
})
export class SubmissionFormFooterComponent extends BaseComponent {
diff --git a/src/themes/custom/app/submission/form/submission-form.component.ts b/src/themes/custom/app/submission/form/submission-form.component.ts
index 1c2cf07d09..d175b7b9c9 100644
--- a/src/themes/custom/app/submission/form/submission-form.component.ts
+++ b/src/themes/custom/app/submission/form/submission-form.component.ts
@@ -1,6 +1,13 @@
+import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
+import { ThemedLoadingComponent } from '../../../../../app/shared/loading/themed-loading.component';
+import { SubmissionFormCollectionComponent } from '../../../../../app/submission/form/collection/submission-form-collection.component';
+import { ThemedSubmissionFormFooterComponent } from '../../../../../app/submission/form/footer/themed-submission-form-footer.component';
+import { SubmissionFormSectionAddComponent } from '../../../../../app/submission/form/section-add/submission-form-section-add.component';
import { SubmissionFormComponent as BaseComponent } from '../../../../../app/submission/form/submission-form.component';
+import { ThemedSubmissionUploadFilesComponent } from '../../../../../app/submission/form/submission-upload-files/themed-submission-upload-files.component';
+import { ThemedSubmissionSectionContainerComponent } from '../../../../../app/submission/sections/container/themed-section-container.component';
@Component({
selector: 'ds-themed-submission-form',
@@ -9,6 +16,15 @@ import { SubmissionFormComponent as BaseComponent } from '../../../../../app/sub
// templateUrl: './submission-form.component.html'
templateUrl: '../../../../../app/submission/form/submission-form.component.html',
standalone: true,
+ imports: [
+ CommonModule,
+ ThemedLoadingComponent,
+ ThemedSubmissionSectionContainerComponent,
+ ThemedSubmissionFormFooterComponent,
+ ThemedSubmissionUploadFilesComponent,
+ SubmissionFormCollectionComponent,
+ SubmissionFormSectionAddComponent,
+ ],
})
export class SubmissionFormComponent extends BaseComponent {
diff --git a/src/themes/custom/app/submission/sections/container/section-container.component.ts b/src/themes/custom/app/submission/sections/container/section-container.component.ts
index f24d679fb9..0dfe51946c 100644
--- a/src/themes/custom/app/submission/sections/container/section-container.component.ts
+++ b/src/themes/custom/app/submission/sections/container/section-container.component.ts
@@ -1,6 +1,15 @@
+import {
+ AsyncPipe,
+ NgClass,
+ NgComponentOutlet,
+} from '@angular/common';
import { Component } from '@angular/core';
+import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateModule } from '@ngx-translate/core';
+import { AlertComponent } from '../../../../../../app/shared/alert/alert.component';
import { SubmissionSectionContainerComponent as BaseComponent } from '../../../../../../app/submission/sections/container/section-container.component';
+import { SectionsDirective } from '../../../../../../app/submission/sections/sections.directive';
@Component({
selector: 'ds-themed-submission-upload-section-file',
@@ -9,6 +18,15 @@ import { SubmissionSectionContainerComponent as BaseComponent } from '../../../.
// templateUrl: './section-container.component.html'
templateUrl: '../../../../../../app/submission/sections/container/section-container.component.html',
standalone: true,
+ imports: [
+ AlertComponent,
+ NgbAccordionModule,
+ NgComponentOutlet,
+ TranslateModule,
+ NgClass,
+ AsyncPipe,
+ SectionsDirective,
+ ],
})
export class SubmissionSectionContainerComponent extends BaseComponent {
From 2fc84783a54afa9fb454ad75606831d4ce1b3746 Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Fri, 28 Mar 2025 15:49:37 +0200
Subject: [PATCH 13/54] 127047: Fix submission-form.component.spec.ts
---
.../form/submission-form.component.spec.ts | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/app/submission/form/submission-form.component.spec.ts b/src/app/submission/form/submission-form.component.spec.ts
index f097a1e7ea..5f0528003a 100644
--- a/src/app/submission/form/submission-form.component.spec.ts
+++ b/src/app/submission/form/submission-form.component.spec.ts
@@ -10,13 +10,18 @@ import {
TestBed,
waitForAsync,
} from '@angular/core/testing';
+import { Store } from '@ngrx/store';
import {
cold,
getTestScheduler,
} from 'jasmine-marbles';
-import { of as observableOf } from 'rxjs';
+import {
+ of as observableOf,
+ of,
+} from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
+import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface';
import { AuthService } from '../../core/auth/auth.service';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { Item } from '../../core/shared/item.model';
@@ -56,6 +61,8 @@ describe('SubmissionFormComponent Component', () => {
let scheduler: TestScheduler;
const submissionServiceStub: SubmissionServiceStub = new SubmissionServiceStub();
+ submissionServiceStub.getSubmissionStatus = jasmine.createSpy('getSubmissionStatus')
+ .and.returnValue(of(true));
const submissionId = mockSubmissionId;
const collectionId = mockSubmissionCollectionId;
const submissionObjectNew: any = mockSubmissionObjectNew;
@@ -64,6 +71,7 @@ describe('SubmissionFormComponent Component', () => {
const selfUrl: any = mockSubmissionSelfUrl;
const sectionsList: any = mockSectionsList;
const sectionsData: any = mockSectionsData;
+ const store = jasmine.createSpyObj('store', ['dispatch']);
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
@@ -75,6 +83,8 @@ describe('SubmissionFormComponent Component', () => {
{ provide: SubmissionService, useValue: submissionServiceStub },
{ provide: SectionsService, useValue: { isSectionTypeAvailable: () => observableOf(true) } },
{ provide: ThemeService, useValue: getMockThemeService() },
+ { provide: Store, useValue: store },
+ { provide: APP_DATA_SERVICES_MAP, useValue: {} },
ChangeDetectorRef,
SubmissionFormComponent,
],
From 502597aadc68ef6f565c48d0a75c62ab12513bbf Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Fri, 4 Apr 2025 13:20:01 +0300
Subject: [PATCH 14/54] 127047: Fix submission-edit.component.spec.ts
---
src/app/submission/edit/submission-edit.component.spec.ts | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/app/submission/edit/submission-edit.component.spec.ts b/src/app/submission/edit/submission-edit.component.spec.ts
index 59f9883f19..3ef6c7045d 100644
--- a/src/app/submission/edit/submission-edit.component.spec.ts
+++ b/src/app/submission/edit/submission-edit.component.spec.ts
@@ -57,6 +57,8 @@ describe('SubmissionEditComponent Component', () => {
const submissionObject: any = mockSubmissionObject;
beforeEach(waitForAsync(() => {
+ // Fix for missing CSS custom property
+ document.documentElement.style.setProperty('--bs-xl', '1200');
itemDataService = jasmine.createSpyObj('itemDataService', {
findByHref: createSuccessfulRemoteDataObject$(submissionObject.item),
});
@@ -105,6 +107,10 @@ describe('SubmissionEditComponent Component', () => {
});
afterEach(() => {
+ if (fixture) {
+ // Ensure Angular cleans up the component properly
+ fixture.destroy();
+ }
comp = null;
fixture = null;
router = null;
From 7864909466c0423718b37fa06ede3d86cdbd9ab2 Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Tue, 15 Apr 2025 12:03:25 +0300
Subject: [PATCH 15/54] 127047: Fix selector of
SubmissionSectionContainerComponent in custom theme
---
.../sections/container/section-container.component.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/themes/custom/app/submission/sections/container/section-container.component.ts b/src/themes/custom/app/submission/sections/container/section-container.component.ts
index 0dfe51946c..aa548c0a9c 100644
--- a/src/themes/custom/app/submission/sections/container/section-container.component.ts
+++ b/src/themes/custom/app/submission/sections/container/section-container.component.ts
@@ -12,7 +12,7 @@ import { SubmissionSectionContainerComponent as BaseComponent } from '../../../.
import { SectionsDirective } from '../../../../../../app/submission/sections/sections.directive';
@Component({
- selector: 'ds-themed-submission-upload-section-file',
+ selector: 'ds-themed-base-submission-section-container',
// styleUrls: ['./section-container.component.scss'],
styleUrls: ['../../../../../../app/submission/sections/container/section-container.component.scss'],
// templateUrl: './section-container.component.html'
From d269d668e5436caa2f93f3cb758b3a9dab29fc5a Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Tue, 22 Apr 2025 09:57:27 +0300
Subject: [PATCH 16/54] 127047: Remove default value of collectionModifiable
and Update overrideComponent to exclude themed components
---
.../form/submission-form.component.spec.ts | 20 +++++--------------
.../form/themed-submission-form.component.ts | 2 +-
2 files changed, 6 insertions(+), 16 deletions(-)
diff --git a/src/app/submission/form/submission-form.component.spec.ts b/src/app/submission/form/submission-form.component.spec.ts
index 5f0528003a..f1136946ee 100644
--- a/src/app/submission/form/submission-form.component.spec.ts
+++ b/src/app/submission/form/submission-form.component.spec.ts
@@ -10,18 +10,13 @@ import {
TestBed,
waitForAsync,
} from '@angular/core/testing';
-import { Store } from '@ngrx/store';
import {
cold,
getTestScheduler,
} from 'jasmine-marbles';
-import {
- of as observableOf,
- of,
-} from 'rxjs';
+import { of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
-import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface';
import { AuthService } from '../../core/auth/auth.service';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { Item } from '../../core/shared/item.model';
@@ -42,12 +37,12 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic
import { SubmissionServiceStub } from '../../shared/testing/submission-service.stub';
import { createTestComponent } from '../../shared/testing/utils.test';
import { ThemeService } from '../../shared/theme-support/theme.service';
-import { SubmissionSectionContainerComponent } from '../sections/container/section-container.component';
+import { ThemedSubmissionSectionContainerComponent } from '../sections/container/themed-section-container.component';
import { SectionsService } from '../sections/sections.service';
import { VisibilityType } from '../sections/visibility-type';
import { SubmissionService } from '../submission.service';
import { SubmissionFormCollectionComponent } from './collection/submission-form-collection.component';
-import { SubmissionFormFooterComponent } from './footer/submission-form-footer.component';
+import { ThemedSubmissionFormFooterComponent } from './footer/themed-submission-form-footer.component';
import { SubmissionFormSectionAddComponent } from './section-add/submission-form-section-add.component';
import { SubmissionFormComponent } from './submission-form.component';
import { ThemedSubmissionUploadFilesComponent } from './submission-upload-files/themed-submission-upload-files.component';
@@ -61,8 +56,6 @@ describe('SubmissionFormComponent Component', () => {
let scheduler: TestScheduler;
const submissionServiceStub: SubmissionServiceStub = new SubmissionServiceStub();
- submissionServiceStub.getSubmissionStatus = jasmine.createSpy('getSubmissionStatus')
- .and.returnValue(of(true));
const submissionId = mockSubmissionId;
const collectionId = mockSubmissionCollectionId;
const submissionObjectNew: any = mockSubmissionObjectNew;
@@ -71,7 +64,6 @@ describe('SubmissionFormComponent Component', () => {
const selfUrl: any = mockSubmissionSelfUrl;
const sectionsList: any = mockSectionsList;
const sectionsData: any = mockSectionsData;
- const store = jasmine.createSpyObj('store', ['dispatch']);
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
@@ -83,8 +75,6 @@ describe('SubmissionFormComponent Component', () => {
{ provide: SubmissionService, useValue: submissionServiceStub },
{ provide: SectionsService, useValue: { isSectionTypeAvailable: () => observableOf(true) } },
{ provide: ThemeService, useValue: getMockThemeService() },
- { provide: Store, useValue: store },
- { provide: APP_DATA_SERVICES_MAP, useValue: {} },
ChangeDetectorRef,
SubmissionFormComponent,
],
@@ -94,8 +84,8 @@ describe('SubmissionFormComponent Component', () => {
remove: {
imports: [
ThemedLoadingComponent,
- SubmissionSectionContainerComponent,
- SubmissionFormFooterComponent,
+ ThemedSubmissionSectionContainerComponent,
+ ThemedSubmissionFormFooterComponent,
ThemedSubmissionUploadFilesComponent,
SubmissionFormCollectionComponent,
SubmissionFormSectionAddComponent,
diff --git a/src/app/submission/form/themed-submission-form.component.ts b/src/app/submission/form/themed-submission-form.component.ts
index b7414aa8e9..4a6f8d67cd 100644
--- a/src/app/submission/form/themed-submission-form.component.ts
+++ b/src/app/submission/form/themed-submission-form.component.ts
@@ -22,7 +22,7 @@ export class ThemedSubmissionFormComponent extends ThemedComponent
Date: Tue, 22 Apr 2025 12:58:33 +0200
Subject: [PATCH 17/54] [DURACOM-353] fix orejime callbacks
---
.../cookies/browser-orejime.service.spec.ts | 27 +++++++++++++++++
.../shared/cookies/browser-orejime.service.ts | 30 +++++++++++++++++--
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/src/app/shared/cookies/browser-orejime.service.spec.ts b/src/app/shared/cookies/browser-orejime.service.spec.ts
index 126e113227..b0a108e17d 100644
--- a/src/app/shared/cookies/browser-orejime.service.spec.ts
+++ b/src/app/shared/cookies/browser-orejime.service.spec.ts
@@ -433,4 +433,31 @@ describe('BrowserOrejimeService', () => {
expect(service.orejimeConfig.apps).not.toContain(jasmine.objectContaining({ name: googleAnalytics }));
});
});
+
+ describe('applyUpdateSettingsCallbackToApps', () => {
+ let user2: EPerson;
+ let mockApp1, mockApp2;
+ let updateSettingsSpy;
+
+ beforeEach(() => {
+ user2 = Object.assign(new EPerson(), { uuid: 'test-user' });
+ mockApp1 = { name: 'app1', callback: jasmine.createSpy('originalCallback1') };
+ mockApp2 = { name: 'app2', callback: jasmine.createSpy('originalCallback2') };
+ service.orejimeConfig.apps = [mockApp1, mockApp2];
+ updateSettingsSpy = spyOn(service, 'updateSettingsForUsers');
+ });
+
+ it('calls updateSettingsForUsers in a debounced manner when a callback is triggered', (done) => {
+ service.applyUpdateSettingsCallbackToApps(user2);
+
+ mockApp1.callback(true);
+ mockApp2.callback(false);
+
+ setTimeout(() => {
+ expect(updateSettingsSpy).toHaveBeenCalledTimes(1);
+ expect(updateSettingsSpy).toHaveBeenCalledWith(user2);
+ done();
+ }, 400);
+ });
+ });
});
diff --git a/src/app/shared/cookies/browser-orejime.service.ts b/src/app/shared/cookies/browser-orejime.service.ts
index b2d3b6a4c0..e30576ee20 100644
--- a/src/app/shared/cookies/browser-orejime.service.ts
+++ b/src/app/shared/cookies/browser-orejime.service.ts
@@ -192,12 +192,39 @@ export class BrowserOrejimeService extends OrejimeService {
this.translateConfiguration();
this.orejimeConfig.apps = this.filterConfigApps(appsToHide);
- this.lazyOrejime.then(({ init }) => {
+
+ this.applyUpdateSettingsCallbackToApps(user);
+
+ void this.lazyOrejime.then(({ init }) => {
this.orejimeInstance = init(this.orejimeConfig);
});
});
}
+ /**
+ * Applies a debounced callback to update user settings for all apps in the Orejime configuration.
+ *
+ * This method modifies the `callback` property of each app in the `orejimeConfig.apps` array.
+ * It ensures that the `updateSettingsForUsers` method is called in a debounced manner whenever
+ * a consent change occurs for any app. Additionally, it preserves and invokes the original
+ * callback for each app if one is defined.
+ *
+ * @param {EPerson} user - The authenticated user whose settings are being updated.
+ */
+ applyUpdateSettingsCallbackToApps(user: EPerson) {
+ const updateSettingsCallback = debounce(() => this.updateSettingsForUsers(user), updateDebounce);
+
+ this.orejimeConfig.apps.forEach((app) => {
+ const originalCallback = app.callback;
+ app.callback = (consent: boolean) => {
+ updateSettingsCallback();
+ if (originalCallback) {
+ originalCallback(consent);
+ }
+ };
+ });
+ }
+
/**
* Return saved preferences stored in the orejime cookie
*/
@@ -220,7 +247,6 @@ export class BrowserOrejimeService extends OrejimeService {
* @param user The authenticated user
*/
private initializeUser(user: EPerson) {
- this.orejimeConfig.callback = debounce((consent, app) => this.updateSettingsForUsers(user), updateDebounce);
this.orejimeConfig.cookieName = this.getStorageName(user.uuid);
const anonCookie = this.cookieService.get(ANONYMOUS_STORAGE_NAME_OREJIME);
From b89a626ffcfc660566fc75ca33c77a1232a149c9 Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Thu, 17 Apr 2025 17:30:59 +0200
Subject: [PATCH 18/54] #4172 Several fixes to RSS component and object lists
* RSS button displayed on search result pages
* RSS button displayed on Recent Items component
* RSS button NOT displayed in other components
like top-level community, etc.
* RSS component tracks sort option changes
---
.../recent-item-list.component.html | 1 +
.../recent-item-list.component.ts | 3 +-
.../object-collection.component.html | 2 +
.../object-collection.component.ts | 9 +-
.../object-detail.component.html | 1 +
.../object-detail/object-detail.component.ts | 2 +
.../object-list/object-list.component.html | 1 +
.../object-list/object-list.component.ts | 7 +-
.../themed-object-list.component.ts | 3 +
.../pagination/pagination.component.html | 4 +-
.../shared/pagination/pagination.component.ts | 8 +-
src/app/shared/rss-feed/rss.component.ts | 155 +++++++++++-------
.../search-results.component.html | 1 +
13 files changed, 134 insertions(+), 63 deletions(-)
diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.html b/src/app/home-page/recent-item-list/recent-item-list.component.html
index 4d77e5027e..7a7ecdacce 100644
--- a/src/app/home-page/recent-item-list/recent-item-list.component.html
+++ b/src/app/home-page/recent-item-list/recent-item-list.component.html
@@ -3,6 +3,7 @@
{{'home.recent-submissions.head' | translate}}
+
@for (item of itemRD?.payload?.page; track item) {
diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.ts b/src/app/home-page/recent-item-list/recent-item-list.component.ts
index b336cf7e9b..1855eed909 100644
--- a/src/app/home-page/recent-item-list/recent-item-list.component.ts
+++ b/src/app/home-page/recent-item-list/recent-item-list.component.ts
@@ -41,6 +41,7 @@ import { ErrorComponent } from '../../shared/error/error.component';
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
import { ListableObjectComponentLoaderComponent } from '../../shared/object-collection/shared/listable-object/listable-object-component-loader.component';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { RSSComponent } from '../../shared/rss-feed/rss.component';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
import {
followLink,
@@ -59,7 +60,7 @@ import { VarDirective } from '../../shared/utils/var.directive';
fadeInOut,
],
standalone: true,
- imports: [VarDirective, NgClass, ListableObjectComponentLoaderComponent, ErrorComponent, ThemedLoadingComponent, AsyncPipe, TranslateModule],
+ imports: [VarDirective, NgClass, ListableObjectComponentLoaderComponent, ErrorComponent, ThemedLoadingComponent, AsyncPipe, TranslateModule, RSSComponent],
})
export class RecentItemListComponent implements OnInit, OnDestroy {
itemRD$: Observable>>;
diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html
index 10378ba3f5..1d9be701d2 100644
--- a/src/app/shared/object-collection/object-collection.component.html
+++ b/src/app/shared/object-collection/object-collection.component.html
@@ -13,6 +13,7 @@
[linkType]="linkType"
[context]="context"
[hidePaginationDetail]="hidePaginationDetail"
+ [showRSS]="showRSS"
[showPaginator]="showPaginator"
[showThumbnails]="showThumbnails"
(paginationChange)="onPaginationChange($event)"
@@ -58,6 +59,7 @@
[sortConfig]="sortConfig"
[objects]="objects"
[hideGear]="hideGear"
+ [showRSS]="showRSS"
[linkType]="linkType"
[context]="context"
[hidePaginationDetail]="hidePaginationDetail"
diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts
index 204685930a..c7849adbff 100644
--- a/src/app/shared/object-collection/object-collection.component.ts
+++ b/src/app/shared/object-collection/object-collection.component.ts
@@ -71,17 +71,22 @@ export class ObjectCollectionComponent implements OnInit {
@Input() sortConfig: SortOptions;
/**
- * Whether or not the list elements have a border or not
+ * Whether the list elements have a border or not
*/
@Input() hasBorder = false;
/**
- * Whether or not to hide the gear to change the sort and pagination configuration
+ * Whether to hide the gear to change the sort and pagination configuration
*/
@Input() hideGear = false;
@Input() selectable = false;
@Input() selectionConfig: {repeatable: boolean, listId: string};
+ /**
+ * Whether to show an RSS syndication button for the current search options
+ */
+ @Input() showRSS = false;
+
/**
* Emit custom event for listable object custom actions.
*/
diff --git a/src/app/shared/object-detail/object-detail.component.html b/src/app/shared/object-detail/object-detail.component.html
index ad9f0bce0e..2696730cf6 100644
--- a/src/app/shared/object-detail/object-detail.component.html
+++ b/src/app/shared/object-detail/object-detail.component.html
@@ -4,6 +4,7 @@
[sortOptions]="sortConfig"
[objects]="objects"
[hideGear]="hideGear"
+ [showRSS]="showRSS"
[hidePaginationDetail]="hidePaginationDetail"
[hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
[showPaginator]="showPaginator"
diff --git a/src/app/shared/object-detail/object-detail.component.ts b/src/app/shared/object-detail/object-detail.component.ts
index 01e1795fcb..eae56a217d 100644
--- a/src/app/shared/object-detail/object-detail.component.ts
+++ b/src/app/shared/object-detail/object-detail.component.ts
@@ -85,6 +85,8 @@ export class ObjectDetailComponent {
*/
@Input() showThumbnails;
+ @Input() showRSS = false;
+
/**
* Emit when one of the listed object has changed.
*/
diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html
index 3073623c2e..5670199fc5 100644
--- a/src/app/shared/object-list/object-list.component.html
+++ b/src/app/shared/object-list/object-list.component.html
@@ -4,6 +4,7 @@
[objects]="objects"
[sortOptions]="sortConfig"
[hideGear]="hideGear"
+ [showRSS]="showRSS"
[hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
[hidePaginationDetail]="hidePaginationDetail"
[showPaginator]="showPaginator"
diff --git a/src/app/shared/object-list/object-list.component.ts b/src/app/shared/object-list/object-list.component.ts
index cf3e0164f6..19d3cc9906 100644
--- a/src/app/shared/object-list/object-list.component.ts
+++ b/src/app/shared/object-list/object-list.component.ts
@@ -39,7 +39,7 @@ import { SelectableListService } from './selectable-list/selectable-list.service
})
export class ObjectListComponent {
/**
- * The view mode of the this component
+ * The view mode of this component
*/
viewMode = ViewMode.ListElement;
@@ -70,6 +70,11 @@ export class ObjectListComponent {
@Input() selectable = false;
@Input() selectionConfig: { repeatable: boolean, listId: string };
+ /**
+ * Whether to show an RSS syndication button for the current search options
+ */
+ @Input() showRSS = false;
+
/**
* The link type of the listable elements
*/
diff --git a/src/app/shared/object-list/themed-object-list.component.ts b/src/app/shared/object-list/themed-object-list.component.ts
index e6e0527c90..65309678bf 100644
--- a/src/app/shared/object-list/themed-object-list.component.ts
+++ b/src/app/shared/object-list/themed-object-list.component.ts
@@ -59,6 +59,8 @@ export class ThemedObjectListComponent extends ThemedComponent
}
-
+ @if (showRSS) {
+
+ }
diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts
index 907b427354..7392d8f91e 100644
--- a/src/app/shared/pagination/pagination.component.ts
+++ b/src/app/shared/pagination/pagination.component.ts
@@ -168,6 +168,13 @@ export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
*/
@Input() public retainScrollPosition = false;
+ /**
+ * Options for showing or hiding the RSS syndication feed. This is useful for e.g. top-level community lists
+ * or other lists where an RSS feed doesn't make sense, but uses the same components as recent items or search result
+ * lists.
+ */
+ @Input() public showRSS = false;
+
/**
* Current page.
*/
@@ -266,7 +273,6 @@ export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
* Initializes all default variables
*/
private initializeConfig() {
- // Set initial values
this.id = this.paginationOptions.id || null;
this.pageSizeOptions = this.paginationOptions.pageSizeOptions;
this.currentPage$ = this.paginationService.getCurrentPagination(this.id, this.paginationOptions).pipe(
diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts
index 8d2b49f0e1..8c81f7975e 100644
--- a/src/app/shared/rss-feed/rss.component.ts
+++ b/src/app/shared/rss-feed/rss.component.ts
@@ -2,12 +2,16 @@ import { AsyncPipe } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
+ Input,
+ OnChanges,
OnDestroy,
OnInit,
+ SimpleChanges,
ViewEncapsulation,
} from '@angular/core';
import {
ActivatedRoute,
+ NavigationEnd,
Router,
} from '@angular/router';
import {
@@ -16,12 +20,10 @@ import {
} from '@ngx-translate/core';
import {
BehaviorSubject,
+ filter,
Subscription,
} from 'rxjs';
-import {
- map,
- switchMap,
-} from 'rxjs/operators';
+import { map } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
import { SortOptions } from '../../core/cache/models/sort-options.model';
@@ -31,13 +33,14 @@ import { GroupDataService } from '../../core/eperson/group-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { LinkHeadService } from '../../core/services/link-head.service';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
+import { SearchService } from '../../core/shared/search/search.service';
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
import {
hasValue,
isUndefined,
} from '../empty.util';
-import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
import { SearchFilter } from '../search/models/search-filter.model';
+
/**
* The Rss feed button component.
*/
@@ -51,30 +54,28 @@ import { SearchFilter } from '../search/models/search-filter.model';
standalone: true,
imports: [AsyncPipe, TranslateModule],
})
-export class RSSComponent implements OnInit, OnDestroy {
+export class RSSComponent implements OnInit, OnDestroy, OnChanges {
route$: BehaviorSubject = new BehaviorSubject('');
-
isEnabled$: BehaviorSubject = new BehaviorSubject(null);
-
isActivated$: BehaviorSubject = new BehaviorSubject(false);
+ @Input() sortConfig?: SortOptions;
uuid: string;
-
subs: Subscription[] = [];
+ openSearchUri: string;
constructor(private groupDataService: GroupDataService,
private linkHeadService: LinkHeadService,
private configurationService: ConfigurationDataService,
private searchConfigurationService: SearchConfigurationService,
+ private searchService: SearchService,
private router: Router,
private route: ActivatedRoute,
protected paginationService: PaginationService,
protected translateService: TranslateService) {
}
- /**
- * Removes the linktag created when the component gets removed from the page.
- */
+
ngOnDestroy(): void {
this.linkHeadService.removeTag("rel='alternate'");
this.subs.forEach(sub => {
@@ -82,24 +83,52 @@ export class RSSComponent implements OnInit, OnDestroy {
});
}
-
- /**
- * Generates the link tags and the url to opensearch when the component is loaded.
- */
ngOnInit(): void {
+ // Set initial activation state
if (hasValue(this.route.snapshot.data?.enableRSS)) {
this.isActivated$.next(this.route.snapshot.data.enableRSS);
} else if (isUndefined(this.route.snapshot.data?.enableRSS)) {
this.isActivated$.next(false);
}
+
+ // Get initial UUID from URL
+ this.uuid = this.groupDataService.getUUIDFromString(this.router.url);
+
+ // Check if RSS is enabled
this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.enable').pipe(
getFirstCompletedRemoteData(),
).subscribe((result) => {
if (result.hasSucceeded) {
const enabled = (result.payload.values[0] === 'true');
this.isEnabled$.next(enabled);
+
+ // If enabled, get the OpenSearch URI
+ if (enabled) {
+ this.getOpenSearchUri();
+ }
}
}));
+
+ // Listen for navigation events to update the UUID
+ this.subs.push(this.router.events.pipe(
+ filter(event => event instanceof NavigationEnd),
+ ).subscribe(() => {
+ this.uuid = this.groupDataService.getUUIDFromString(this.router.url);
+ this.updateRssLinks();
+ }));
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ // If sortConfig changes, update the RSS links
+ if (changes.sortConfig && this.openSearchUri && this.isEnabled$.getValue()) {
+ this.updateRssLinks();
+ }
+ }
+
+ /**
+ * Get the OpenSearch URI and update RSS links
+ */
+ private getOpenSearchUri(): void {
this.subs.push(this.configurationService.findByPropertyName('websvc.opensearch.svccontext').pipe(
getFirstCompletedRemoteData(),
map((result: RemoteData) => {
@@ -108,35 +137,60 @@ export class RSSComponent implements OnInit, OnDestroy {
}
return null;
}),
- switchMap((openSearchUri: string) =>
- this.searchConfigurationService.paginatedSearchOptions.pipe(
- map((searchOptions: PaginatedSearchOptions) => ({ openSearchUri, searchOptions })),
- ),
- ),
- ).subscribe(({ openSearchUri, searchOptions }) => {
- if (!openSearchUri) {
- return null;
- }
- this.uuid = this.groupDataService.getUUIDFromString(this.router.url);
- const route = environment.rest.baseUrl + this.formulateRoute(this.uuid, openSearchUri, searchOptions.sort, searchOptions.query, searchOptions.filters, searchOptions.configuration, searchOptions.pagination?.pageSize, searchOptions.fixedFilter);
- this.addLinks(route);
- this.linkHeadService.addTag({
- href: environment.rest.baseUrl + '/' + openSearchUri + '/service',
- type: 'application/atom+xml',
- rel: 'search',
- title: 'Dspace',
- });
- this.route$.next(route);
+ filter(uri => !!uri),
+ ).subscribe(uri => {
+ this.openSearchUri = uri;
+ this.updateRssLinks();
}));
}
/**
- * Function created a route given the different params available to opensearch
- * @param uuid The uuid if a scope is present
- * @param opensearch openSearch uri
- * @param sort The sort options for the opensearch request
- * @param query The query string that was provided in the search
- * @returns The combine URL to opensearch
+ * Update RSS links based on current search configuration and sortConfig input
+ */
+ private updateRssLinks(): void {
+ if (!this.openSearchUri || !this.isEnabled$.getValue()) {
+ return;
+ }
+
+ // Remove existing link tags before adding new ones
+ this.linkHeadService.removeTag("rel='alternate'");
+
+ // Get the current search options and apply our sortConfig if provided
+ const searchOptions = this.searchConfigurationService.paginatedSearchOptions.value;
+ const modifiedOptions = { ...searchOptions };
+ if (hasValue(this.sortConfig)) {
+ modifiedOptions.sort = this.sortConfig;
+ }
+
+ // Create the RSS feed URL
+ const route = environment.rest.baseUrl + this.formulateRoute(
+ this.uuid,
+ this.openSearchUri,
+ modifiedOptions.sort,
+ modifiedOptions.query,
+ modifiedOptions.filters,
+ modifiedOptions.configuration,
+ modifiedOptions.pagination?.pageSize,
+ modifiedOptions.fixedFilter,
+ );
+
+ // Add the link tags
+ this.addLinks(route);
+
+ // Add the OpenSearch service link
+ this.linkHeadService.addTag({
+ href: environment.rest.baseUrl + '/' + this.openSearchUri + '/service',
+ type: 'application/atom+xml',
+ rel: 'search',
+ title: 'Dspace',
+ });
+
+ // Update the route subject
+ this.route$.next(route);
+ }
+
+ /**
+ * Create a route given the different params available to opensearch
*/
formulateRoute(uuid: string, opensearch: string, sort?: SortOptions, query?: string, searchFilters?: SearchFilter[], configuration?: string, pageSize?: number, fixedFilter?: string): string {
let route = 'format=atom';
@@ -158,9 +212,9 @@ export class RSSComponent implements OnInit, OnDestroy {
route += `&rpp=${pageSize}`;
}
if (searchFilters) {
- for (const filter of searchFilters) {
- for (const val of filter.values) {
- route += '&' + filter.key + '=' + encodeURIComponent(val) + (filter.operator ? ',' + filter.operator : '');
+ for (const searchFilter of searchFilters) {
+ for (const val of searchFilter.values) {
+ route += '&' + searchFilter.key + '=' + encodeURIComponent(val) + (searchFilter.operator ? ',' + searchFilter.operator : '');
}
}
}
@@ -171,20 +225,8 @@ export class RSSComponent implements OnInit, OnDestroy {
return route;
}
- /**
- * Check if the router url contains the specified route
- *
- * @param {string} route
- * @returns
- * @memberof MyComponent
- */
- hasRoute(route: string) {
- return this.router.url.includes(route);
- }
-
/**
* Creates tags in the header of the page
- * @param route The composed url to opensearch
*/
addLinks(route: string): void {
this.linkHeadService.addTag({
@@ -201,5 +243,4 @@ export class RSSComponent implements OnInit, OnDestroy {
title: 'Sitewide RSS feed',
});
}
-
}
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 0026d0ea5b..75e5fdf9b4 100644
--- a/src/app/shared/search/search-results/search-results.component.html
+++ b/src/app/shared/search/search-results/search-results.component.html
@@ -25,6 +25,7 @@
[sortConfig]="searchConfig.sort"
[objects]="searchResults"
[hideGear]="true"
+ [showRSS]="true"
[selectable]="selectable"
[selectionConfig]="selectionConfig"
[linkType]="linkType"
From 3a347d83b5a8cf2e5d424c1fa7f35eafbd5dd9a2 Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Thu, 24 Apr 2025 15:09:19 +0200
Subject: [PATCH 19/54] #4172 Allow SortOptions override to showRSS input
If a valid, complete SortOptions is passed to
the pagination component showRSS, it will be
used instead of the underlying sortOptions,
otherwise the pagination context sortOptions
will be used.
---
.../top-level-community-list.component.html | 1 +
.../top-level-community-list.component.ts | 4 +++-
.../object-collection.component.ts | 2 +-
.../object-detail/object-detail.component.ts | 5 ++++-
.../shared/object-list/object-list.component.ts | 2 +-
.../object-list/themed-object-list.component.ts | 2 +-
.../shared/pagination/pagination.component.html | 2 +-
.../shared/pagination/pagination.component.ts | 17 ++++++++++++++++-
8 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/src/app/home-page/top-level-community-list/top-level-community-list.component.html b/src/app/home-page/top-level-community-list/top-level-community-list.component.html
index 00446d658a..8b0ee4b853 100644
--- a/src/app/home-page/top-level-community-list/top-level-community-list.component.html
+++ b/src/app/home-page/top-level-community-list/top-level-community-list.component.html
@@ -8,6 +8,7 @@
diff --git a/src/app/home-page/top-level-community-list/top-level-community-list.component.ts b/src/app/home-page/top-level-community-list/top-level-community-list.component.ts
index 4af7bcc483..7f3e384df8 100644
--- a/src/app/home-page/top-level-community-list/top-level-community-list.component.ts
+++ b/src/app/home-page/top-level-community-list/top-level-community-list.component.ts
@@ -65,9 +65,10 @@ export class TopLevelCommunityListComponent implements OnInit, OnDestroy {
pageId = 'tl';
/**
- * The sorting configuration
+ * The sorting configuration for the community list itself, and the optional RSS feed button
*/
sortConfig: SortOptions;
+ rssSortConfig: SortOptions;
/**
* The subscription to the observable for the current page.
@@ -84,6 +85,7 @@ export class TopLevelCommunityListComponent implements OnInit, OnDestroy {
this.config.pageSize = appConfig.homePage.topLevelCommunityList.pageSize;
this.config.currentPage = 1;
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
+ this.rssSortConfig = new SortOptions('dc.date.accessioned', SortDirection.DESC);
}
ngOnInit() {
diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts
index c7849adbff..d44c0153b2 100644
--- a/src/app/shared/object-collection/object-collection.component.ts
+++ b/src/app/shared/object-collection/object-collection.component.ts
@@ -85,7 +85,7 @@ export class ObjectCollectionComponent implements OnInit {
/**
* Whether to show an RSS syndication button for the current search options
*/
- @Input() showRSS = false;
+ @Input() showRSS: SortOptions | boolean = false;
/**
* Emit custom event for listable object custom actions.
diff --git a/src/app/shared/object-detail/object-detail.component.ts b/src/app/shared/object-detail/object-detail.component.ts
index eae56a217d..67037aa6bd 100644
--- a/src/app/shared/object-detail/object-detail.component.ts
+++ b/src/app/shared/object-detail/object-detail.component.ts
@@ -85,7 +85,10 @@ export class ObjectDetailComponent {
*/
@Input() showThumbnails;
- @Input() showRSS = false;
+ /**
+ * Whether to show the RSS syndication link. Either false, or valid SortOptions object
+ */
+ @Input() showRSS: SortOptions | boolean = false;
/**
* Emit when one of the listed object has changed.
diff --git a/src/app/shared/object-list/object-list.component.ts b/src/app/shared/object-list/object-list.component.ts
index 19d3cc9906..3c0136fec9 100644
--- a/src/app/shared/object-list/object-list.component.ts
+++ b/src/app/shared/object-list/object-list.component.ts
@@ -73,7 +73,7 @@ export class ObjectListComponent {
/**
* Whether to show an RSS syndication button for the current search options
*/
- @Input() showRSS = false;
+ @Input() showRSS: SortOptions | boolean = false;
/**
* The link type of the listable elements
diff --git a/src/app/shared/object-list/themed-object-list.component.ts b/src/app/shared/object-list/themed-object-list.component.ts
index 65309678bf..df932e3a0f 100644
--- a/src/app/shared/object-list/themed-object-list.component.ts
+++ b/src/app/shared/object-list/themed-object-list.component.ts
@@ -59,7 +59,7 @@ export class ThemedObjectListComponent extends ThemedComponent
}
@if (showRSS) {
-
+
}
diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts
index 7392d8f91e..a10373af1e 100644
--- a/src/app/shared/pagination/pagination.component.ts
+++ b/src/app/shared/pagination/pagination.component.ts
@@ -173,7 +173,7 @@ export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
* or other lists where an RSS feed doesn't make sense, but uses the same components as recent items or search result
* lists.
*/
- @Input() public showRSS = false;
+ @Input() public showRSS: SortOptions | boolean = false;
/**
* Current page.
@@ -442,4 +442,19 @@ export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
});
}
+ /**
+ * Get the sort options to use for the RSS feed. Defaults to the sort options used for this pagination component
+ * so it matches the search/browse context, but also allows more flexibility if, for example a top-level community
+ * list is displayed in "title asc" order, but the RSS feed should default to an item list of "date desc" order.
+ * If the SortOptions are null, incomplete or invalid, the pagination sortOptions will be used instead.
+ */
+ get rssSortOptions() {
+ if (this.showRSS !== false && this.showRSS instanceof SortOptions
+ && this.showRSS.direction !== null
+ && this.showRSS.field !== null) {
+ return this.showRSS;
+ }
+ return this.sortOptions;
+ }
+
}
From 6f878e5c4a3004c234111128bb3fd05dddb0ca92 Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Thu, 24 Apr 2025 15:16:32 +0200
Subject: [PATCH 20/54] #4172 Better boolean comparison of showRSS test
---
src/app/shared/pagination/pagination.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html
index 6cfa5c456b..03898a1664 100644
--- a/src/app/shared/pagination/pagination.component.html
+++ b/src/app/shared/pagination/pagination.component.html
@@ -43,7 +43,7 @@
}
- @if (showRSS) {
+ @if (showRSS !== false) {
}
From e3c6ad807b3baead4f57a505322d207dac5499d9 Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Thu, 24 Apr 2025 16:18:41 +0200
Subject: [PATCH 21/54] #4172 Remove unneeded SearchService from RSS
---
src/app/shared/rss-feed/rss.component.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/app/shared/rss-feed/rss.component.ts b/src/app/shared/rss-feed/rss.component.ts
index 8c81f7975e..1ed29697bf 100644
--- a/src/app/shared/rss-feed/rss.component.ts
+++ b/src/app/shared/rss-feed/rss.component.ts
@@ -33,7 +33,6 @@ import { GroupDataService } from '../../core/eperson/group-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { LinkHeadService } from '../../core/services/link-head.service';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
-import { SearchService } from '../../core/shared/search/search.service';
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
import {
hasValue,
@@ -69,7 +68,6 @@ export class RSSComponent implements OnInit, OnDestroy, OnChanges {
private linkHeadService: LinkHeadService,
private configurationService: ConfigurationDataService,
private searchConfigurationService: SearchConfigurationService,
- private searchService: SearchService,
private router: Router,
private route: ActivatedRoute,
protected paginationService: PaginationService,
From f8cfb74555894e8e5982d63bd3007bab19d4cd84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oscar=20Chaco=CC=81n?=
Date: Thu, 24 Apr 2025 13:29:01 -0600
Subject: [PATCH 22/54] tab navigation improved
---
.../breadcrumbs/breadcrumbs.component.html | 2 +-
.../browse-by-taxonomy.component.html | 4 ++-
.../community-list.component.html | 12 +++++---
...-search-result-grid-element.component.html | 4 +--
...-search-result-grid-element.component.html | 4 +--
...-search-result-grid-element.component.html | 4 +--
...-search-result-list-element.component.html | 4 +--
...-search-result-list-element.component.html | 4 +--
...-search-result-list-element.component.html | 4 +--
.../journal-issue.component.html | 2 +-
.../journal-volume.component.html | 2 +-
.../item-pages/journal/journal.component.html | 2 +-
...-search-result-grid-element.component.html | 4 +--
...-search-result-grid-element.component.html | 4 +--
...-search-result-grid-element.component.html | 4 +--
...-search-result-list-element.component.html | 4 +--
...-search-result-list-element.component.html | 4 +--
...-search-result-list-element.component.html | 4 +--
.../org-unit/org-unit.component.html | 2 +-
.../item-pages/person/person.component.html | 2 +-
.../item-pages/project/project.component.html | 2 +-
src/app/footer/footer.component.html | 16 +++++-----
src/app/header/header.component.html | 2 +-
.../home-news/home-news.component.html | 2 +-
.../recent-item-list.component.html | 2 +-
.../alerts/item-alerts.component.html | 2 +-
.../collections/collections.component.html | 4 ++-
.../metadata-uri-values.component.html | 2 +-
.../metadata-values.component.html | 6 ++--
.../publication/publication.component.html | 2 +-
.../untyped-item/untyped-item.component.html | 2 +-
.../objectnotfound.component.html | 2 +-
src/app/page-error/page-error.component.html | 2 +-
.../pagenotfound/pagenotfound.component.html | 2 +-
.../grant-deny-request-copy.component.html | 2 +-
.../search-navbar.component.html | 2 +-
.../auth-nav-menu.component.html | 6 ++--
.../comcol-page-browse-by.component.html | 4 ++-
.../comcol-page-handle.component.html | 2 +-
.../file-download-link.component.html | 4 ++-
.../vocabulary-treeview.component.html | 30 +++++++++++++------
.../log-in-external-provider.component.html | 2 +-
.../password/log-in-password.component.html | 6 ++--
.../menu-item/link-menu-item.component.html | 1 +
.../menu-item/text-menu-item.component.html | 2 +-
.../browse-entry-list-element.component.html | 2 +-
.../collection-list-element.component.html | 2 +-
.../community-list-element.component.html | 2 +-
...-text-metadata-list-element.component.html | 6 ++--
...-search-result-list-element.component.html | 4 +--
.../pagination/pagination.component.html | 10 +++----
.../results-back-button.component.html | 2 +-
src/app/shared/rss-feed/rss.component.html | 2 +-
.../search-form/search-form.component.html | 6 ++--
.../search-authority-filter.component.html | 4 +--
.../search-boolean-filter.component.html | 4 +--
.../search-facet-option.component.html | 2 +-
.../search-facet-range-option.component.html | 3 +-
...earch-facet-selected-option.component.html | 2 +-
.../search-filter.component.html | 2 ++
.../search-hierarchy-filter.component.html | 6 ++--
.../search-range-filter.component.html | 4 +--
.../search-text-filter.component.html | 4 +--
.../search-results.component.html | 2 +-
.../date/starts-with-date.component.html | 2 +-
.../text/starts-with-text.component.html | 2 +-
.../truncatable-part.component.html | 1 +
.../dspace/app/header/header.component.html | 2 +-
.../home-news/home-news.component.html | 4 +--
69 files changed, 149 insertions(+), 116 deletions(-)
diff --git a/src/app/breadcrumbs/breadcrumbs.component.html b/src/app/breadcrumbs/breadcrumbs.component.html
index cbf9f0c886..b16f46f082 100644
--- a/src/app/breadcrumbs/breadcrumbs.component.html
+++ b/src/app/breadcrumbs/breadcrumbs.component.html
@@ -12,7 +12,7 @@
}
-
+
diff --git a/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.html b/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.html
index f533757d68..6b10260e92 100644
--- a/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.html
+++ b/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.html
@@ -20,6 +20,8 @@
+ [queryParamsHandling]="'merge'"
+ role="link"
+ tabindex="0">
{{ 'browse.taxonomy.button' | translate }}
diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html
index 864b5032d5..9b8eb46703 100644
--- a/src/app/community-list-page/community-list/community-list.component.html
+++ b/src/app/community-list-page/community-list/community-list.component.html
@@ -12,7 +12,7 @@
@if ((dataSource.loading$ | async) !== true) {
+ class="btn btn-outline-primary btn-sm" role="button" tabindex="0">
{{ 'communityList.showMore' | translate }}
}
@@ -34,7 +34,11 @@
+ (keyup.enter)="toggleExpanded(node)"
+ (keyup.space)="toggleExpanded(node)"
+ data-test="expand-button"
+ role="button"
+ tabindex="0">
{{ (node.isExpanded ? 'communityList.collapse' : 'communityList.expand') | translate:{ name: dsoNameService.getName(node.payload) } }}
@@ -48,7 +52,7 @@
}
diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html
index 786e3b5466..82242676be 100644
--- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html
+++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html
@@ -7,7 +7,7 @@
+ class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate" role="link" tabindex="0">
diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html
index 22903638d3..da5bc8bd68 100644
--- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html
+++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html
@@ -7,7 +7,7 @@
+ class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate" role="link" tabindex="0">
diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html
index 18a5ffc5cc..d0e95ab142 100644
--- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html
+++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html
@@ -7,7 +7,7 @@
+ class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate" role="link" tabindex="0">
diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html
index 1807147bdb..a78edc2765 100644
--- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html
+++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html
@@ -4,7 +4,7 @@
@if (linkType !== linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out" role="link" tabindex="0">
@@ -26,7 +26,7 @@
+ [innerHTML]="dsoTitle" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out" role="link" tabindex="0">
@@ -26,7 +26,7 @@
+ [innerHTML]="dsoTitle" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
@if (linkType !== linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out" role="link" tabindex="0">
@@ -24,7 +24,7 @@
@if (linkType !== linkTypes.None) {
+ [innerHTML]="dsoTitle" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html
index 13c8e12939..7c2c71ebfd 100644
--- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html
+++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html
@@ -36,7 +36,7 @@
[label]="'journalvolume.page.description'">
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
index ddd15b5573..19e11cdea6 100644
--- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
+++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
@@ -35,7 +35,7 @@
[label]="'journal.page.description'">
diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html
index 2836fbfbe1..28efef8fd3 100644
--- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html
+++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html
@@ -7,7 +7,7 @@
+ class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate" role="link" tabindex="0">
diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html
index f3613a8637..6e1b78a81e 100644
--- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html
+++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html
@@ -7,7 +7,7 @@
+ class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate" role="link" tabindex="0">
diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html
index 0c266c09dc..46f9566272 100644
--- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html
+++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html
@@ -7,7 +7,7 @@
+ class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate" role="link" tabindex="0">
diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html
index 6c6218dc43..328c73db64 100644
--- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html
+++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html
@@ -4,7 +4,7 @@
@if (linkType !== linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="dont-break-out" role="link" tabindex="0">
+ [innerHTML]="dsoTitle || ('orgunit.listelement.no-title' | translate)" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="dont-break-out" role="link" tabindex="0">
+ [innerHTML]="dsoTitle || ('person.listelement.no-title' | translate)" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="dont-break-out" role="link" tabindex="0">
+ [innerHTML]="dsoTitle" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html
index efeec4bc04..c9abb7a560 100644
--- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html
+++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html
@@ -52,7 +52,7 @@
[label]="'person.page.name'">
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html
index 86c49a7608..f94079f3c2 100644
--- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html
+++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html
@@ -64,7 +64,7 @@
[label]="'project.page.keyword'">
diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html
index bd18fc2a37..1a995aaf01 100644
--- a/src/app/footer/footer.component.html
+++ b/src/app/footer/footer.component.html
@@ -26,7 +26,7 @@
Footer Content
@@ -53,40 +53,40 @@
{{ 'footer.link.dspace' | translate}}
+ href="http://www.dspace.org/" role="link" tabindex="0">{{ 'footer.link.dspace' | translate}}
{{ 'footer.copyright' | translate:{year: dateObj | date:'y'} }}
{{ 'footer.link.lyrasis' | translate}}
+ href="https://www.lyrasis.org/" role="link" tabindex="0">{{ 'footer.link.lyrasis' | translate}}
@if (coarLdnEnabled$ | async) {
-
+
{{ 'footer.link.coar-notify-support' | translate }}
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
index 67a4a96c23..dc3d7f94b9 100644
--- a/src/app/header/header.component.html
+++ b/src/app/header/header.component.html
@@ -1,7 +1,7 @@
-
+
diff --git a/src/app/home-page/home-news/home-news.component.html b/src/app/home-page/home-news/home-news.component.html
index 972c8cc293..9a226ca83f 100644
--- a/src/app/home-page/home-news/home-news.component.html
+++ b/src/app/home-page/home-news/home-news.component.html
@@ -14,7 +14,7 @@
issue permanent urls and trustworthy identifiers, including optional integrations with handle.net and DataCite DOI
Join an international community of leading institutions using DSpace .
+ target="_blank" role="link" tabindex="0">leading institutions using DSpace.
diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.html b/src/app/home-page/recent-item-list/recent-item-list.component.html
index 4d77e5027e..1a7fc342ed 100644
--- a/src/app/home-page/recent-item-list/recent-item-list.component.html
+++ b/src/app/home-page/recent-item-list/recent-item-list.component.html
@@ -9,7 +9,7 @@
}
- {{'vocabulary-treeview.load-more' | translate }} ...
+ {{'vocabulary-treeview.load-more' | translate }} ...
}
@if (itemRD?.hasFailed) {
diff --git a/src/app/item-page/alerts/item-alerts.component.html b/src/app/item-page/alerts/item-alerts.component.html
index cb69f873bb..0f2205232b 100644
--- a/src/app/item-page/alerts/item-alerts.component.html
+++ b/src/app/item-page/alerts/item-alerts.component.html
@@ -10,7 +10,7 @@
{{'item.alerts.withdrawn' | translate}}
-
{{"404.link.home-page" | translate}}
+
{{"404.link.home-page" | translate}}
@if (showReinstateButton$ | async) {
{{ 'item.alerts.reinstate-request' | translate}}
}
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 f5f42bd272..88d50a1f33 100644
--- a/src/app/item-page/field-components/collections/collections.component.html
+++ b/src/app/item-page/field-components/collections/collections.component.html
@@ -1,7 +1,7 @@
@for (collection of (this.collections$ | async); track collection; let last = $last) {
-
+
{{ dsoNameService.getName(collection) }} @if (!last) {
}
@@ -21,6 +21,8 @@
class="load-more-btn btn btn-sm btn-outline-secondary"
role="button"
href="javascript:void(0);"
+ role="button"
+ 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 2093318e66..c07e85d5db 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,6 +1,6 @@
@for (mdValue of mdValues; track mdValue; let last = $last) {
-
+
{{ linktext || mdValue.value }}@if (!last) {
}
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 015398f041..60fca0a8b7 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
@@ -23,14 +23,14 @@
+ [attr.rel]="getLinkAttributes(value).rel" role="link" tabindex="0">
{{value}}
-
+
{{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 31bb741a9f..673e58c785 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
@@ -116,7 +116,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 d941bb6327..77d4168a1c 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
@@ -104,7 +104,7 @@
diff --git a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.html b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.html
index e1cf58b5b2..e3416f8405 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 @@
{{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 9a5f02600a..9a8ffc7e90 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." + 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 e85316b0ec..8177323a7b 100644
--- a/src/app/pagenotfound/pagenotfound.component.html
+++ b/src/app/pagenotfound/pagenotfound.component.html
@@ -5,6 +5,6 @@
{{"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 4a5e91273b..e62eca8941 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
@@ -42,7 +42,7 @@
}
diff --git a/src/app/search-navbar/search-navbar.component.html b/src/app/search-navbar/search-navbar.component.html
index a13abcdf53..d3148cf37f 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 4dac66b749..4082718920 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
@@ -8,6 +8,7 @@
@if ((isAuthenticated | async) !== true) {
-
+
{{ 'nav.login' | translate }}(current)
}
@if ((isAuthenticated | async)) {
-
+
(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 c88bb16afc..49641261e0 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
@@ -10,7 +10,9 @@
role="tab"
[routerLink]="option.routerLink"
[queryParams]="option.params"
- [class.active]="(currentOption$ | async)?.id === option.id">
+ [class.active]="(currentOption$ | async)?.id === option.id"
+ role="link"
+ 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 64f3c3c96b..e5be7732a4 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
@@ -3,6 +3,6 @@
@if (title) {
{{ 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 a03836c9f1..bb9144ce99 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
@@ -5,7 +5,9 @@
[queryParams]="(bitstreamPath$| async)?.queryParams"
[target]="isBlank ? '_blank': '_self'"
[ngClass]="cssClasses"
- [attr.aria-label]="('file-download-link.download' | translate) + dsoNameService.getName(bitstream)">
+ [attr.aria-label]="('file-download-link.download' | translate) + dsoNameService.getName(bitstream)"
+ role="link"
+ tabindex="0">
@if ((canDownload$ | async) === false && (canDownloadWithToken$ | async) === false) {
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 1aa293ed77..1a35ed525c 100644
--- a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html
+++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html
@@ -9,21 +9,21 @@
[placeholder]="'vocabulary-treeview.search.form.search-placeholder' | translate">
+ [attr.aria-label]="'vocabulary-treeview.search.form.search' | translate" role="button" tabindex="0">
{{'vocabulary-treeview.search.form.search' | translate}}
+ [attr.aria-label]="'vocabulary-treeview.search.form.reset' | translate" role="button" tabindex="0">
{{'vocabulary-treeview.search.form.reset' | translate}}
@if (showAdd && this.vocabularyOptions.closed) {
+ [attr.aria-label]="'vocabulary-treeview.search.form.add' | translate" role="button" tabindex="0">
{{'vocabulary-treeview.search.form.add' | translate}}
}
+ [attr.aria-label]="'vocabulary-treeview.search.form.add' | translate" role="button" tabindex="0">
{{'vocabulary-treeview.search.form.add' | translate}}
@@ -59,6 +59,8 @@
[(ngModel)]="node.isSelected"
[checked]="node.isSelected"
(change)="onSelect(node.item)"
+ role="checkbox"
+ tabindex="0"
>
{{node.item.display}}
@@ -70,7 +72,9 @@
[ngbTooltip]="node.item?.otherInformation?.note"
[openDelay]="500"
container="body"
- (click)="onSelect(node.item)">
+ (click)="onSelect(node.item)"
+ role="button"
+ tabindex="0">
{{node.item.display}}
}
@@ -80,7 +84,11 @@
+ (click)="loadChildren(node)"
+ (keydown.enter)="loadChildren(node)"
+ (keydown.space)="loadChildren(node)"
+ role="button"
+ tabindex="0">
@@ -95,6 +103,8 @@
[(ngModel)]="node.isSelected"
[checked]="node.isSelected"
(change)="onSelect(node.item)"
+ role="checkbox"
+ tabindex="0"
>
{{node.item.display}}
@@ -106,7 +116,9 @@
[ngbTooltip]="node.item?.otherInformation?.note"
[openDelay]="500"
container="body"
- (click)="onSelect(node.item)">
+ (click)="onSelect(node.item)"
+ role="button"
+ tabindex="0">
{{node.item.display}}
}
@@ -114,14 +126,14 @@
+ [attr.aria-label]="'vocabulary-treeview.load-more' | translate" role="button" tabindex="0">
{{'vocabulary-treeview.load-more' | translate}}...
+ [attr.aria-label]="'vocabulary-treeview.load-more' | translate" role="button" tabindex="0">
{{'vocabulary-treeview.load-more' | translate}}...
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 a8d511a3f5..5a079f91a6 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 @@
-
+
{{getButtonLabel() | translate}}
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 52685c361c..5eb2088a81 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
@@ -28,18 +28,18 @@
}
{{"login.form.submit" | translate}}
+ [dsBtnDisabled]="!form.valid" role="button" tabindex="0"> {{"login.form.submit" | translate}}
@if (canShowDivider$ | async) {
}
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 71eeda2e68..f96084e6e1 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 ba3cf99a49..e2dd334caf 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 @@
-
+
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 e0bae4ec57..524243f23d 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,6 +1,6 @@
@if (linkType !== linkTypes.None) {
-
+
{{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 74f77225c1..a49f328ff3 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,6 +1,6 @@
@if (linkType !== linkTypes.None) {
-
+
{{ 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 3c7faf3dab..90cb409819 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,6 +1,6 @@
@if (linkType !== linkTypes.None) {
-
+
{{ 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 ecaec7ff64..8c550d0276 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
@@ -7,7 +7,7 @@
}
@if ((mdRepresentation.representationType==='plain_text') && isLink()) {
+ target="_blank" [href]="mdRepresentation.getValue()" role="link" tabindex="0">
{{mdRepresentation.getValue()}}
}
@@ -18,7 +18,9 @@
+ [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 db647b6e74..cceb69e1ed 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
@@ -3,7 +3,7 @@
@if (linkType !== linkTypes.None) {
+ [routerLink]="[itemPageRoute]" class="dont-break-out" role="button" tabindex="0">
@@ -28,7 +28,7 @@
@if (linkType !== linkTypes.None) {
+ [innerHTML]="dsoTitle" role="link" tabindex="0">
}
@if (linkType === linkTypes.None) {
@if (!hideGear) {
-
+
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 b25a602996..1ed8ba0fc6 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
@@ -13,13 +13,13 @@
@if ((isLastPage$ | async) !== true) {
+ (click)="showMore()" href="javascript:void(0);" role="button" tabindex="0">
{{"search.filters.filter.show-more" | translate}}
}
@if ((currentPage | async) > 1) {
+ (click)="showFirstPageOnly()" href="javascript:void(0);" role="button" tabindex="0">
{{"search.filters.filter.show-less" | translate}}
}
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 c5abc198a6..bd8d2c32fe 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
@@ -13,13 +13,13 @@
@if ((isLastPage$ | async) !== true) {
+ (click)="showMore()" href="javascript:void(0);" role="button" tabindex="0">
{{"search.filters.filter.show-more" | translate}}
}
@if ((currentPage | async) > 1) {
+ (click)="showFirstPageOnly()" href="javascript:void(0);" role="button" tabindex="0">
{{"search.filters.filter.show-less" | translate}}
}
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 75387550b2..767de25ae8 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
@@ -5,7 +5,7 @@
[queryParams]="addQueryParams$ | async"
(click)="announceFilter(); filterService.minimizeAll()">
-
+
{{filterValue.count | dsShortNumber}}
{{ 'search.filters.' + filterConfig.name + '.' + filterValue.value | translate: {default: filterValue.value} }}
diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html
index 595609d5a4..40247215ce 100644
--- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html
+++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html
@@ -2,7 +2,8 @@
+ [queryParams]="changeQueryParams" queryParamsHandling="merge"
+ role="button" tabindex="0">
{{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 b1045a854a..786740bacc 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
@@ -4,7 +4,7 @@
[queryParams]="removeQueryParams | async"
(click)="searchFilterService.minimizeAll()">
-
+
{{ 'search.filters.' + filterConfig.name + '.' + selectedValue.value | translate: {default: selectedValue.label} }}
diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-filter.component.html
index 32821d27b6..d71ec2f598 100644
--- a/src/app/shared/search/search-filters/search-filter/search-filter.component.html
+++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.html
@@ -6,6 +6,8 @@
[attr.aria-expanded]="(collapsed$ | async) !== true"
[attr.aria-label]="(((collapsed$ | async) ? 'search.filters.filter.expand' : 'search.filters.filter.collapse') | translate) + ' ' + (('search.filters.filter.' + filter.name + '.head') | translate | lowercase)"
[attr.data-test]="'filter-toggle' | dsBrowserOnly"
+ role="button"
+ tabindex="0"
>
{{'search.filters.filter.' + filter.name + '.head'| translate}}
diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html
index 1774aba0a0..5759993829 100644
--- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html
+++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html
@@ -13,13 +13,13 @@
-
+
{{'search.filters.search.submit' | translate}}
@@ -46,7 +46,7 @@
}
}
-
+
{{'search.filters.search.submit' | translate}}
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 b25a602996..1ed8ba0fc6 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
@@ -13,13 +13,13 @@
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 cd07a898c0..7c80d357a8 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
@@ -32,7 +32,7 @@
- {{ 'browse.startsWith.submit' | translate }}
+ {{ 'browse.startsWith.submit' | translate }}
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 5208427f34..6e70dc122b 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
@@ -3,7 +3,7 @@
{{'browse.startsWith.type_text' | translate}}
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 fef02ea6d7..a0ec379f54 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 7007954e7d..8aea10a98e 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">
-
+
@if ((isMobile$ | async) !== true) {
diff --git a/src/themes/dspace/app/home-page/home-news/home-news.component.html b/src/themes/dspace/app/home-page/home-news/home-news.component.html
index 6734942852..7ccf4fe74f 100644
--- a/src/themes/dspace/app/home-page/home-news/home-news.component.html
+++ b/src/themes/dspace/app/home-page/home-news/home-news.component.html
@@ -4,7 +4,7 @@
DSpace 9
-
This site is running DSpace 9. For more information, see the DSpace 9 Release Notes .
+
This site is running DSpace 9. For more information, see the DSpace 9 Release Notes .
DSpace is the world leading open source repository platform that enables
organisations to:
@@ -20,7 +20,7 @@
handle.net and DataCite DOI
-
Join an international community of leading institutions using DSpace .
+
Join an international community of leading institutions using DSpace .
The test user accounts below have their password set to the name of this
software in lowercase.
From 4e5b344ce8c5224713fde612b3440d0181871361 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oscar=20Chaco=CC=81n?=
Date: Thu, 24 Apr 2025 17:11:54 -0600
Subject: [PATCH 23/54] hotfix: e2e accessibility tests
---
.../comcol-page-browse-by/comcol-page-browse-by.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 49641261e0..1758fa542c 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
@@ -11,7 +11,7 @@
[routerLink]="option.routerLink"
[queryParams]="option.params"
[class.active]="(currentOption$ | async)?.id === option.id"
- role="link"
+ role="tab"
tabindex="0">
{{ option.label | translate }}
From b00f489d0979ed7c2162dc8f7bd4d04468456576 Mon Sep 17 00:00:00 2001
From: Joran De Braekeleer
Date: Fri, 25 Apr 2025 10:00:35 +0200
Subject: [PATCH 24/54] 127705: Reduce margins on input fields
---
.../ds-dynamic-form-control-container.component.scss | 6 +++++-
.../models/list/dynamic-list.component.html | 4 ++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.scss
index 494e405404..c0c0305581 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.scss
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.scss
@@ -40,5 +40,9 @@
.col-form-label {
padding-top: 0;
padding-bottom: 0;
- margin-bottom: 0.5rem;
+ margin-bottom: 0.25rem;
+}
+
+label {
+ margin-bottom: 0.25rem;
}
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.html
index c4aade2ed7..574b9eb711 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.html
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.html
@@ -7,7 +7,7 @@
[formGroupName]="model.id"
[ngClass]="model.layout.element?.control">
@for (columnItems of items; track columnItems) {
-
+
@for (item of columnItems; track item) {
@for (columnItems of items; track columnItems) {
-
+
@for (item of columnItems; track item) {
Date: Mon, 28 Apr 2025 02:43:08 +0000
Subject: [PATCH 25/54] Bump webpack from 5.99.6 to 5.99.7 in the webpack group
Bumps the webpack group with 1 update: [webpack](https://github.com/webpack/webpack).
Updates `webpack` from 5.99.6 to 5.99.7
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.99.6...v5.99.7)
---
updated-dependencies:
- dependency-name: webpack
dependency-version: 5.99.7
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: webpack
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 18 ++++++++++--------
package.json | 2 +-
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 689928b1d7..98a2f3963a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -155,7 +155,7 @@
"sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2",
"typescript": "~5.4.5",
- "webpack": "5.99.6",
+ "webpack": "5.99.7",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
@@ -20807,10 +20807,11 @@
}
},
"node_modules/schema-utils": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
- "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
+ "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
@@ -22771,14 +22772,15 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/webpack": {
- "version": "5.99.6",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
- "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
+ "version": "5.99.7",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.7.tgz",
+ "integrity": "sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
"@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-edit": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.14.1",
@@ -22795,7 +22797,7 @@
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
- "schema-utils": "^4.3.0",
+ "schema-utils": "^4.3.2",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.11",
"watchpack": "^2.4.1",
diff --git a/package.json b/package.json
index f9846a6b62..a66f0527eb 100644
--- a/package.json
+++ b/package.json
@@ -237,7 +237,7 @@
"sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2",
"typescript": "~5.4.5",
- "webpack": "5.99.6",
+ "webpack": "5.99.7",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
From 3d8951db18f6ac580863fbfa9abfbce489c72ad3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 02:43:22 +0000
Subject: [PATCH 26/54] Bump axios from 1.8.4 to 1.9.0
Bumps [axios](https://github.com/axios/axios) from 1.8.4 to 1.9.0.
- [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.8.4...v1.9.0)
---
updated-dependencies:
- dependency-name: axios
dependency-version: 1.9.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 8 ++++----
package.json | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 689928b1d7..94d019ffaf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,7 +35,7 @@
"@terraformer/wkt": "^2.2.1",
"altcha": "^0.9.0",
"angulartics2": "^12.2.0",
- "axios": "^1.8.4",
+ "axios": "^1.9.0",
"bootstrap": "^5.3",
"cerialize": "0.1.18",
"cli-progress": "^3.12.0",
@@ -8830,9 +8830,9 @@
}
},
"node_modules/axios": {
- "version": "1.8.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
- "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
+ "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
diff --git a/package.json b/package.json
index f9846a6b62..963b9dd7b0 100644
--- a/package.json
+++ b/package.json
@@ -117,7 +117,7 @@
"@terraformer/wkt": "^2.2.1",
"altcha": "^0.9.0",
"angulartics2": "^12.2.0",
- "axios": "^1.8.4",
+ "axios": "^1.9.0",
"bootstrap": "^5.3",
"cerialize": "0.1.18",
"cli-progress": "^3.12.0",
From 25f474386b248b91375addf9c1252eeec3ef6911 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 02:49:17 +0000
Subject: [PATCH 27/54] Bump isbot from 5.1.26 to 5.1.27
Bumps [isbot](https://github.com/omrilotan/isbot) from 5.1.26 to 5.1.27.
- [Changelog](https://github.com/omrilotan/isbot/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omrilotan/isbot/compare/v5.1.26...v5.1.27)
---
updated-dependencies:
- dependency-name: isbot
dependency-version: 5.1.27
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 8 ++++----
package.json | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 689928b1d7..13fb4b5caa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -53,7 +53,7 @@
"filesize": "^10.1.6",
"http-proxy-middleware": "^2.0.9",
"http-terminator": "^3.2.0",
- "isbot": "^5.1.26",
+ "isbot": "^5.1.27",
"js-cookie": "2.2.1",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
@@ -14731,9 +14731,9 @@
}
},
"node_modules/isbot": {
- "version": "5.1.26",
- "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.26.tgz",
- "integrity": "sha512-3wqJEYSIm59dYQjEF7zJ7T42aqaqxbCyJQda5rKCudJykuAnISptCHR/GSGpOnw8UrvU+mGueNLRJS5HXnbsXQ==",
+ "version": "5.1.27",
+ "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.27.tgz",
+ "integrity": "sha512-V3W56Hnztt4Wdh3VUlAMbdNicX/tOM38eChW3a2ixP6KEBJAeehxzYzTD59JrU5NCTgBZwRt9lRWr8D7eMZVYQ==",
"license": "Unlicense",
"engines": {
"node": ">=18"
diff --git a/package.json b/package.json
index f9846a6b62..5641593242 100644
--- a/package.json
+++ b/package.json
@@ -135,7 +135,7 @@
"filesize": "^10.1.6",
"http-proxy-middleware": "^2.0.9",
"http-terminator": "^3.2.0",
- "isbot": "^5.1.26",
+ "isbot": "^5.1.27",
"js-cookie": "2.2.1",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
From e4d53eddccf20a5a0fed8329b976fb7122982af6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 02:49:37 +0000
Subject: [PATCH 28/54] Bump @ngtools/webpack from 18.2.18 to 18.2.19
Bumps [@ngtools/webpack](https://github.com/angular/angular-cli) from 18.2.18 to 18.2.19.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/18.2.18...18.2.19)
---
updated-dependencies:
- dependency-name: "@ngtools/webpack"
dependency-version: 18.2.19
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 25 +++++++++++++++++++++----
package.json | 2 +-
2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 689928b1d7..ac303be074 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -100,7 +100,7 @@
"@cypress/schematic": "^1.5.0",
"@fortawesome/fontawesome-free": "^6.7.2",
"@ngrx/store-devtools": "^18.1.1",
- "@ngtools/webpack": "^18.2.18",
+ "@ngtools/webpack": "^18.2.19",
"@types/deep-freeze": "0.1.5",
"@types/ejs": "^3.1.2",
"@types/express": "^4.17.17",
@@ -491,6 +491,23 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": {
+ "version": "18.2.18",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.18.tgz",
+ "integrity": "sha512-rFTf3zrAMp7KJF8F/sOn0SNits+HhRaNKw4g20Pxk4QG5XZsXChsQIKrrzAnmlCfMb3nQmBnElAhr1rvBmzZWQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler-cli": "^18.0.0",
+ "typescript": ">=5.4 <5.6",
+ "webpack": "^5.54.0"
+ }
+ },
"node_modules/@angular-devkit/build-angular/node_modules/@types/retry": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
@@ -5960,9 +5977,9 @@
}
},
"node_modules/@ngtools/webpack": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.18.tgz",
- "integrity": "sha512-rFTf3zrAMp7KJF8F/sOn0SNits+HhRaNKw4g20Pxk4QG5XZsXChsQIKrrzAnmlCfMb3nQmBnElAhr1rvBmzZWQ==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.19.tgz",
+ "integrity": "sha512-bExj5JrByKPibsqBbn5Pjn8lo91AUOTsyP2hgKpnOnmSr62rhWSiRwXltgz2MCiZRmuUznpt93WiOLixgYfYvQ==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/package.json b/package.json
index f9846a6b62..c035ee0d89 100644
--- a/package.json
+++ b/package.json
@@ -182,7 +182,7 @@
"@cypress/schematic": "^1.5.0",
"@fortawesome/fontawesome-free": "^6.7.2",
"@ngrx/store-devtools": "^18.1.1",
- "@ngtools/webpack": "^18.2.18",
+ "@ngtools/webpack": "^18.2.19",
"@types/deep-freeze": "0.1.5",
"@types/ejs": "^3.1.2",
"@types/express": "^4.17.17",
From 33d75cf217e1e2a3b4712d9156aa69737ec3211e Mon Sep 17 00:00:00 2001
From: Andrea Barbasso <´andrea.barbasso@4science.com´>
Date: Mon, 28 Apr 2025 14:16:13 +0200
Subject: [PATCH 29/54] [DURACOM-353] fix error in SSR
---
src/app/shared/cookies/browser-orejime.service.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/app/shared/cookies/browser-orejime.service.ts b/src/app/shared/cookies/browser-orejime.service.ts
index e30576ee20..1296f4c3c1 100644
--- a/src/app/shared/cookies/browser-orejime.service.ts
+++ b/src/app/shared/cookies/browser-orejime.service.ts
@@ -413,7 +413,9 @@ export class BrowserOrejimeService extends OrejimeService {
* @param user
*/
updateSettingsForUsers(user: EPerson) {
- this.setSettingsForUser(user, this.cookieService.get(this.getStorageName(user.uuid)));
+ if (user) {
+ this.setSettingsForUser(user, this.cookieService.get(this.getStorageName(user.uuid)));
+ }
}
/**
From 72a050c7aecd9fe8d8800e20a67c278fd21e9914 Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Mon, 28 Apr 2025 15:31:31 +0200
Subject: [PATCH 30/54] [#4172] Shift enableRSS route data for coll and comm
routes
app-routes previously set this but it now needs to be
set in collection-page-routes and community-page-routes
respectively.
---
src/app/app-routes.ts | 2 --
src/app/collection-page/collection-page-routes.ts | 1 +
src/app/community-page/community-page-routes.ts | 1 +
3 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/app/app-routes.ts b/src/app/app-routes.ts
index 937947c048..0882f5b72c 100644
--- a/src/app/app-routes.ts
+++ b/src/app/app-routes.ts
@@ -99,14 +99,12 @@ export const APP_ROUTES: Route[] = [
path: COMMUNITY_MODULE_PATH,
loadChildren: () => import('./community-page/community-page-routes')
.then((m) => m.ROUTES),
- data: { enableRSS: true },
canActivate: [endUserAgreementCurrentUserGuard],
},
{
path: COLLECTION_MODULE_PATH,
loadChildren: () => import('./collection-page/collection-page-routes')
.then((m) => m.ROUTES),
- data: { enableRSS: true },
canActivate: [endUserAgreementCurrentUserGuard],
},
{
diff --git a/src/app/collection-page/collection-page-routes.ts b/src/app/collection-page/collection-page-routes.ts
index 9b729f1dc4..118b999240 100644
--- a/src/app/collection-page/collection-page-routes.ts
+++ b/src/app/collection-page/collection-page-routes.ts
@@ -99,6 +99,7 @@ export const ROUTES: Route[] = [
data: {
breadcrumbKey: 'collection.search',
menuRoute: MenuRoute.COLLECTION_PAGE,
+ enableRSS: true,
},
},
{
diff --git a/src/app/community-page/community-page-routes.ts b/src/app/community-page/community-page-routes.ts
index 258fdda049..edbbfa1516 100644
--- a/src/app/community-page/community-page-routes.ts
+++ b/src/app/community-page/community-page-routes.ts
@@ -86,6 +86,7 @@ export const ROUTES: Route[] = [
data: {
breadcrumbKey: 'community.search',
menuRoute: MenuRoute.COMMUNITY_PAGE,
+ enableRSS: true,
},
},
{
From 97a4c3c0c0218fd3f21abe5e90adb21e54b2582b Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Mon, 28 Apr 2025 15:37:55 +0200
Subject: [PATCH 31/54] [#4172] Disable manual ds-rss display in recent items
comp
This can be added in easily with:
` `
---
.../home-page/recent-item-list/recent-item-list.component.html | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.html b/src/app/home-page/recent-item-list/recent-item-list.component.html
index 7a7ecdacce..4d77e5027e 100644
--- a/src/app/home-page/recent-item-list/recent-item-list.component.html
+++ b/src/app/home-page/recent-item-list/recent-item-list.component.html
@@ -3,7 +3,6 @@
{{'home.recent-submissions.head' | translate}}
-
@for (item of itemRD?.payload?.page; track item) {
From cace253d8b61d38c911874c0d775c36d81a1b9c9 Mon Sep 17 00:00:00 2001
From: Andrea Barbasso <´andrea.barbasso@4science.com´>
Date: Mon, 28 Apr 2025 16:15:10 +0200
Subject: [PATCH 32/54] [DURACOM-359] fix matomo downloading its .js even if
it's disabled
---
src/app/statistics/matomo.service.spec.ts | 25 +++++++++++++++++++++++
src/app/statistics/matomo.service.ts | 6 +++---
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/src/app/statistics/matomo.service.spec.ts b/src/app/statistics/matomo.service.spec.ts
index 51ea814231..be14585c2b 100644
--- a/src/app/statistics/matomo.service.spec.ts
+++ b/src/app/statistics/matomo.service.spec.ts
@@ -23,6 +23,7 @@ import {
createSuccessfulRemoteDataObject$,
} from '../shared/remote-data.utils';
import {
+ MATOMO_ENABLED,
MATOMO_SITE_ID,
MATOMO_TRACKER_URL,
MatomoService,
@@ -84,6 +85,9 @@ describe('MatomoService', () => {
configService.findByPropertyName.withArgs(MATOMO_TRACKER_URL).and.returnValue(
createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(),{ values: ['http://matomo'] })),
);
+ configService.findByPropertyName.withArgs(MATOMO_ENABLED).and.returnValue(
+ createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(),{ values: ['true'] })),
+ );
configService.findByPropertyName.withArgs(MATOMO_SITE_ID).and.returnValue(
createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { values: ['1'] })));
orejimeService.getSavedPreferences.and.returnValue(of({ matomo: true }));
@@ -102,6 +106,9 @@ describe('MatomoService', () => {
configService.findByPropertyName.withArgs(MATOMO_TRACKER_URL).and.returnValue(
createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(),{ values: ['http://example.com'] })),
);
+ configService.findByPropertyName.withArgs(MATOMO_ENABLED).and.returnValue(
+ createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(),{ values: ['true'] })),
+ );
configService.findByPropertyName.withArgs(MATOMO_SITE_ID).and.returnValue(
createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { values: ['1'] })));
orejimeService.getSavedPreferences.and.returnValue(of({ matomo: true }));
@@ -123,6 +130,24 @@ describe('MatomoService', () => {
expect(matomoInitializer.initializeTracker).not.toHaveBeenCalled();
});
+ it('should not initialize tracker if matomo is disabled', () => {
+ environment.production = true;
+ environment.matomo = { trackerUrl: '' };
+ configService.findByPropertyName.withArgs(MATOMO_TRACKER_URL).and.returnValue(
+ createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(),{ values: ['http://example.com'] })),
+ );
+ configService.findByPropertyName.withArgs(MATOMO_ENABLED).and.returnValue(
+ createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(),{ values: ['false'] })),
+ );
+ configService.findByPropertyName.withArgs(MATOMO_SITE_ID).and.returnValue(
+ createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { values: ['1'] })));
+ orejimeService.getSavedPreferences.and.returnValue(of({ matomo: true }));
+
+ service.init();
+
+ expect(matomoInitializer.initializeTracker).not.toHaveBeenCalled();
+ });
+
describe('with visitorId set', () => {
beforeEach(() => {
matomoTracker.getVisitorId.and.returnValue(Promise.resolve('12345'));
diff --git a/src/app/statistics/matomo.service.ts b/src/app/statistics/matomo.service.ts
index 88fc7f9476..a30e82f93a 100644
--- a/src/app/statistics/matomo.service.ts
+++ b/src/app/statistics/matomo.service.ts
@@ -77,10 +77,10 @@ export class MatomoService {
preferences$
.pipe(
tap(preferences => this.changeMatomoConsent(preferences?.matomo)),
- switchMap(_ => combineLatest([this.getSiteId$(), this.getTrackerUrl$()])),
+ switchMap(_ => combineLatest([this.isMatomoEnabled$(), this.getSiteId$(), this.getTrackerUrl$()])),
)
- .subscribe(([siteId, trackerUrl]) => {
- if (siteId && trackerUrl) {
+ .subscribe(([isMatomoEnabled, siteId, trackerUrl]) => {
+ if (isMatomoEnabled && siteId && trackerUrl) {
this.matomoInitializer.initializeTracker({ siteId, trackerUrl });
}
});
From a16f1e824892b7f572c131b79ef938fa0dee9fb3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 15:27:45 +0000
Subject: [PATCH 33/54] Bump sass from 1.86.3 to 1.87.0 in the sass group
Bumps the sass group with 1 update: [sass](https://github.com/sass/dart-sass).
Updates `sass` from 1.86.3 to 1.87.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.86.3...1.87.0)
---
updated-dependencies:
- dependency-name: sass
dependency-version: 1.87.0
dependency-type: direct:development
update-type: version-update:semver-minor
dependency-group: sass
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 8 ++++----
package.json | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 23d4ef4539..5d8f62b65f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -150,7 +150,7 @@
"postcss-loader": "^4.0.3",
"postcss-preset-env": "^7.4.2",
"rimraf": "^3.0.2",
- "sass": "~1.86.3",
+ "sass": "~1.87.0",
"sass-loader": "^12.6.0",
"sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2",
@@ -20694,9 +20694,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
- "version": "1.86.3",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.3.tgz",
- "integrity": "sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==",
+ "version": "1.87.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.87.0.tgz",
+ "integrity": "sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 315faf0334..0f8b8346c1 100644
--- a/package.json
+++ b/package.json
@@ -232,7 +232,7 @@
"postcss-loader": "^4.0.3",
"postcss-preset-env": "^7.4.2",
"rimraf": "^3.0.2",
- "sass": "~1.86.3",
+ "sass": "~1.87.0",
"sass-loader": "^12.6.0",
"sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2",
From 62822ade31266423cc9aeda708d42b638bb6d3a3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 15:30:33 +0000
Subject: [PATCH 34/54] Bump the angular group with 3 updates
Bumps the angular group with 3 updates: [@angular/ssr](https://github.com/angular/angular-cli), [@angular-devkit/build-angular](https://github.com/angular/angular-cli) and [@angular/cli](https://github.com/angular/angular-cli).
Updates `@angular/ssr` from 18.2.18 to 18.2.19
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/18.2.18...18.2.19)
Updates `@angular-devkit/build-angular` from 18.2.18 to 18.2.19
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/18.2.18...18.2.19)
Updates `@angular/cli` from 18.2.18 to 18.2.19
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/18.2.18...18.2.19)
---
updated-dependencies:
- dependency-name: "@angular/ssr"
dependency-version: 18.2.19
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: angular
- dependency-name: "@angular-devkit/build-angular"
dependency-version: 18.2.19
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: angular
- dependency-name: "@angular/cli"
dependency-version: 18.2.19
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: angular
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 141 +++++++++++++++++++++-------------------------
package.json | 6 +-
2 files changed, 66 insertions(+), 81 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 23d4ef4539..55ec3bf5ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,7 @@
"@angular/platform-browser-dynamic": "^18.2.12",
"@angular/platform-server": "^18.2.12",
"@angular/router": "^18.2.12",
- "@angular/ssr": "^18.2.18",
+ "@angular/ssr": "^18.2.19",
"@babel/runtime": "7.27.0",
"@kolkov/ngx-gallery": "^2.0.1",
"@ng-bootstrap/ng-bootstrap": "^12.0.0",
@@ -86,7 +86,7 @@
},
"devDependencies": {
"@angular-builders/custom-webpack": "~18.0.0",
- "@angular-devkit/build-angular": "^18.2.18",
+ "@angular-devkit/build-angular": "^18.2.19",
"@angular-eslint/builder": "^18.4.1",
"@angular-eslint/bundled-angular-compiler": "^18.4.1",
"@angular-eslint/eslint-plugin": "^18.4.1",
@@ -94,7 +94,7 @@
"@angular-eslint/schematics": "^18.4.1",
"@angular-eslint/template-parser": "^18.4.1",
"@angular-eslint/utils": "^18.4.1",
- "@angular/cli": "^18.2.18",
+ "@angular/cli": "^18.2.19",
"@angular/compiler-cli": "^18.2.12",
"@angular/language-service": "^18.2.12",
"@cypress/schematic": "^1.5.0",
@@ -266,13 +266,13 @@
}
},
"node_modules/@angular-devkit/architect": {
- "version": "0.1802.18",
- "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.18.tgz",
- "integrity": "sha512-3OitvTddHp7bSqEGOJlH7Zqv07DdmZHktU2jsekjcbUxmoC1WIpWSYy+Bqyu7HjidJc0xVP7wyE/NPYkrwT5SA==",
+ "version": "0.1802.19",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.19.tgz",
+ "integrity": "sha512-M4B1tzxGX1nWCZr9GMM8OO0yBJO2HFSdK8M8P74vEFQfKIeq3y16IQ5zlEveJrkCOFVtmlIy2C9foMCdNyBRMA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@angular-devkit/core": "18.2.18",
+ "@angular-devkit/core": "18.2.19",
"rxjs": "7.8.1"
},
"engines": {
@@ -291,17 +291,17 @@
}
},
"node_modules/@angular-devkit/build-angular": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.18.tgz",
- "integrity": "sha512-yNw5b46BB27YW2lgP9pAt15xtfTS8F1JdWR79bLci0MYL7VPmRBrRtZk+sozRCziit1+oNAVpOUT8QyvDmvAZA==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.19.tgz",
+ "integrity": "sha512-xwY7v+nGE7TXOc4pgY6u57bLzIPSHuecosYr3TiWHAl9iEcKHzkCCFKsLZyunohHmq/i1uA6g3cC6iwp2xNYyg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "2.3.0",
- "@angular-devkit/architect": "0.1802.18",
- "@angular-devkit/build-webpack": "0.1802.18",
- "@angular-devkit/core": "18.2.18",
- "@angular/build": "18.2.18",
+ "@angular-devkit/architect": "0.1802.19",
+ "@angular-devkit/build-webpack": "0.1802.19",
+ "@angular-devkit/core": "18.2.19",
+ "@angular/build": "18.2.19",
"@babel/core": "7.26.10",
"@babel/generator": "7.26.10",
"@babel/helper-annotate-as-pure": "7.25.9",
@@ -312,7 +312,7 @@
"@babel/preset-env": "7.26.9",
"@babel/runtime": "7.26.10",
"@discoveryjs/json-ext": "0.6.1",
- "@ngtools/webpack": "18.2.18",
+ "@ngtools/webpack": "18.2.19",
"ansi-colors": "4.1.3",
"autoprefixer": "10.4.20",
"babel-loader": "9.1.3",
@@ -322,7 +322,7 @@
"css-loader": "7.1.2",
"esbuild-wasm": "0.23.0",
"fast-glob": "3.3.2",
- "http-proxy-middleware": "3.0.3",
+ "http-proxy-middleware": "3.0.5",
"https-proxy-agent": "7.0.5",
"istanbul-lib-instrument": "6.0.3",
"jsonc-parser": "3.3.1",
@@ -418,13 +418,13 @@
}
},
"node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/build-webpack": {
- "version": "0.1802.18",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.18.tgz",
- "integrity": "sha512-xSiUC2EeELKgs70aceet/iK57y2nk6VobgeeQzGzTtE5HXWX0n5/g9FIOVM1rznv/tj+9VFZpQKCdLqiP7JmCQ==",
+ "version": "0.1802.19",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.19.tgz",
+ "integrity": "sha512-axz1Sasn+c+GJpJexBL+B3Rh1w3wJrQq8k8gkniodjJ594p4ti2qGk7i9Tj8A4cXx5fGY+EpuZvKfI/9Tr7QwA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@angular-devkit/architect": "0.1802.18",
+ "@angular-devkit/architect": "0.1802.19",
"rxjs": "7.8.1"
},
"engines": {
@@ -491,23 +491,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.18.tgz",
- "integrity": "sha512-rFTf3zrAMp7KJF8F/sOn0SNits+HhRaNKw4g20Pxk4QG5XZsXChsQIKrrzAnmlCfMb3nQmBnElAhr1rvBmzZWQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- },
- "peerDependencies": {
- "@angular/compiler-cli": "^18.0.0",
- "typescript": ">=5.4 <5.6",
- "webpack": "^5.54.0"
- }
- },
"node_modules/@angular-devkit/build-angular/node_modules/@types/retry": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
@@ -711,10 +694,11 @@
}
},
"node_modules/@angular-devkit/build-angular/node_modules/http-proxy-middleware": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz",
- "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz",
+ "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.15",
"debug": "^4.3.6",
@@ -757,6 +741,7 @@
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -1100,9 +1085,9 @@
}
},
"node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/http-proxy-middleware": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.8.tgz",
- "integrity": "sha512-/iazaeFPmL8KLA6QB7DFAU4O5j+9y/TA0D019MbLtPuFI56VK4BXFzM6j6QS9oGpScy8IIDH4S2LHv3zg/63Bw==",
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
+ "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1166,9 +1151,9 @@
}
},
"node_modules/@angular-devkit/core": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.18.tgz",
- "integrity": "sha512-gncn8QN73mi4in7oAfoWnJglLx5iI8d87796h1LTuAxULSkfzhW3E03NZU764FBiIAWFxuty4PWmrHxMlmbtbw==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.19.tgz",
+ "integrity": "sha512-Ptf92Zomc6FCr7GWmHKdgOUbA1GpctZwH/hRcpYpU3tM56MG2t5FOFpufnE595GgolOCktabkFEoODMG8PBVDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1203,13 +1188,13 @@
}
},
"node_modules/@angular-devkit/schematics": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.18.tgz",
- "integrity": "sha512-i7dy3x32Z8+lmVMKlKHdrSuCya5hUP24BOUn5lXKFAFGcJC0JT30OJrDPqQMA2RzNQiiyacPhxaCdLloEFVh3Q==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.19.tgz",
+ "integrity": "sha512-P/0KjkzOf2ZShuShx3cBbjLI7XlcS6B/yCRBo1MQfCC4cZfmzPQoUEOSQeYZgy5pnC24f+dKh/+TWc5uYL/Lvg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@angular-devkit/core": "18.2.18",
+ "@angular-devkit/core": "18.2.19",
"jsonc-parser": "3.3.1",
"magic-string": "0.30.11",
"ora": "5.4.1",
@@ -1341,14 +1326,14 @@
}
},
"node_modules/@angular/build": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.18.tgz",
- "integrity": "sha512-8PEhrkS1t9xpvBLaLVgi0OWt/0B72ENKKVc6BAKEZ5gg+SD7uf47sJcT1d23r7d/V6FaOJnWim6BrqgFs4rW9A==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.19.tgz",
+ "integrity": "sha512-dTqR+mhcZWtCRyOafvzHNVpYxMQnt8HHHqNM0kyEMzcztXL2L9zDlKr0H9d+AgGGq/v4qwCh+1gFDxsHByZwMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "2.3.0",
- "@angular-devkit/architect": "0.1802.18",
+ "@angular-devkit/architect": "0.1802.19",
"@babel/core": "7.25.2",
"@babel/helper-annotate-as-pure": "7.24.7",
"@babel/helper-split-export-declaration": "7.24.7",
@@ -1814,9 +1799,9 @@
}
},
"node_modules/@angular/build/node_modules/@types/node": {
- "version": "22.14.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz",
- "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==",
+ "version": "22.15.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz",
+ "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1873,9 +1858,9 @@
"peer": true
},
"node_modules/@angular/build/node_modules/vite": {
- "version": "5.4.17",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.17.tgz",
- "integrity": "sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==",
+ "version": "5.4.18",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz",
+ "integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1988,18 +1973,18 @@
}
},
"node_modules/@angular/cli": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.18.tgz",
- "integrity": "sha512-UwwI03FVvTHbb9kgR9D0HdLajxsVm1jYkcWMfbSMnQGYM1qy1EWj9HvGnfIoQxAEzA8aeQbmsn9+h3w6MQmyCg==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.19.tgz",
+ "integrity": "sha512-LGVMTc36JQuw8QX8Sclxyei306EQW3KslopXbf7cfqt6D5/fHS+FqqA0O7V8ob/vOGMca+l6hQD27nW5Y3W6pA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@angular-devkit/architect": "0.1802.18",
- "@angular-devkit/core": "18.2.18",
- "@angular-devkit/schematics": "18.2.18",
+ "@angular-devkit/architect": "0.1802.19",
+ "@angular-devkit/core": "18.2.19",
+ "@angular-devkit/schematics": "18.2.19",
"@inquirer/prompts": "5.3.8",
"@listr2/prompt-adapter-inquirer": "2.0.15",
- "@schematics/angular": "18.2.18",
+ "@schematics/angular": "18.2.19",
"@yarnpkg/lockfile": "1.1.0",
"ini": "4.1.3",
"jsonc-parser": "3.3.1",
@@ -2247,9 +2232,9 @@
}
},
"node_modules/@angular/ssr": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-18.2.18.tgz",
- "integrity": "sha512-WJ56mpiRGp18vcSH4jFHWR6dylBtUk3QOz+RQhuqYFPAfKk2YXEH5BiBXcjNicW5tIxaU8NlHfZVwWHQyEjpiA==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-18.2.19.tgz",
+ "integrity": "sha512-kMNPWZiLGhtrXFwQpDn1laKXxwMpaiXVajpDT7m/yQkyKMH5EbyZASFcyDHK6EsRV2LQsPaXeKzeQof/C1zNcw==",
"license": "MIT",
"dependencies": {
"critters": "0.0.24",
@@ -6975,14 +6960,14 @@
"dev": true
},
"node_modules/@schematics/angular": {
- "version": "18.2.18",
- "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.18.tgz",
- "integrity": "sha512-ko5KmtCZz8SqZLKrNeqMauS2LPHBKf7mT01waoOD1uN2gQkSIiLzDEYuXOaIarG6VnxAy5pL6NjkD+EmPsH6eg==",
+ "version": "18.2.19",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.19.tgz",
+ "integrity": "sha512-s9aynH/fwB/LT94miVfsaL2C4Qd5BLgjMzWFx7iJ8Hyv7FjOBGYO6eGVovjCt2c6/abG+GQAk4EBOCfg3AUtCA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@angular-devkit/core": "18.2.18",
- "@angular-devkit/schematics": "18.2.18",
+ "@angular-devkit/core": "18.2.19",
+ "@angular-devkit/schematics": "18.2.19",
"jsonc-parser": "3.3.1"
},
"engines": {
@@ -11077,9 +11062,9 @@
}
},
"node_modules/detect-libc": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
- "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
diff --git a/package.json b/package.json
index 315faf0334..01ce3d2597 100644
--- a/package.json
+++ b/package.json
@@ -102,7 +102,7 @@
"@angular/platform-browser-dynamic": "^18.2.12",
"@angular/platform-server": "^18.2.12",
"@angular/router": "^18.2.12",
- "@angular/ssr": "^18.2.18",
+ "@angular/ssr": "^18.2.19",
"@babel/runtime": "7.27.0",
"@kolkov/ngx-gallery": "^2.0.1",
"@ng-bootstrap/ng-bootstrap": "^12.0.0",
@@ -168,7 +168,7 @@
},
"devDependencies": {
"@angular-builders/custom-webpack": "~18.0.0",
- "@angular-devkit/build-angular": "^18.2.18",
+ "@angular-devkit/build-angular": "^18.2.19",
"@angular-eslint/builder": "^18.4.1",
"@angular-eslint/bundled-angular-compiler": "^18.4.1",
"@angular-eslint/eslint-plugin": "^18.4.1",
@@ -176,7 +176,7 @@
"@angular-eslint/schematics": "^18.4.1",
"@angular-eslint/template-parser": "^18.4.1",
"@angular-eslint/utils": "^18.4.1",
- "@angular/cli": "^18.2.18",
+ "@angular/cli": "^18.2.19",
"@angular/compiler-cli": "^18.2.12",
"@angular/language-service": "^18.2.12",
"@cypress/schematic": "^1.5.0",
From 6cd88ec57b44d1c3628ad5faecef71aaecfa8df0 Mon Sep 17 00:00:00 2001
From: Kim Shepherd
Date: Mon, 28 Apr 2025 22:46:36 +0200
Subject: [PATCH 35/54] [#4172] Remove unused RSS import from
RecentItemListComponent
---
.../home-page/recent-item-list/recent-item-list.component.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.ts b/src/app/home-page/recent-item-list/recent-item-list.component.ts
index 1855eed909..b336cf7e9b 100644
--- a/src/app/home-page/recent-item-list/recent-item-list.component.ts
+++ b/src/app/home-page/recent-item-list/recent-item-list.component.ts
@@ -41,7 +41,6 @@ import { ErrorComponent } from '../../shared/error/error.component';
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
import { ListableObjectComponentLoaderComponent } from '../../shared/object-collection/shared/listable-object/listable-object-component-loader.component';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
-import { RSSComponent } from '../../shared/rss-feed/rss.component';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
import {
followLink,
@@ -60,7 +59,7 @@ import { VarDirective } from '../../shared/utils/var.directive';
fadeInOut,
],
standalone: true,
- imports: [VarDirective, NgClass, ListableObjectComponentLoaderComponent, ErrorComponent, ThemedLoadingComponent, AsyncPipe, TranslateModule, RSSComponent],
+ imports: [VarDirective, NgClass, ListableObjectComponentLoaderComponent, ErrorComponent, ThemedLoadingComponent, AsyncPipe, TranslateModule],
})
export class RecentItemListComponent implements OnInit, OnDestroy {
itemRD$: Observable>>;
From 3c1d51480730992c563865b0963510f763abd8b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oscar=20Chaco=CC=81n?=
Date: Mon, 28 Apr 2025 23:42:38 -0600
Subject: [PATCH 36/54] fixed flash and translate problems on statistics
---
.../statistics/models/usage-report.model.ts | 2 +-
.../statistics-table.component.html | 4 +-
.../statistics-table.component.spec.ts | 4 +-
.../statistics-table.component.ts | 40 +------------------
4 files changed, 7 insertions(+), 43 deletions(-)
diff --git a/src/app/core/statistics/models/usage-report.model.ts b/src/app/core/statistics/models/usage-report.model.ts
index a4924f3a3d..ea8262065b 100644
--- a/src/app/core/statistics/models/usage-report.model.ts
+++ b/src/app/core/statistics/models/usage-report.model.ts
@@ -32,7 +32,7 @@ export class UsageReport extends HALResource {
id: string;
@autoserializeAs('report-type')
- reportType: string;
+ reportType: string;
@autoserialize
points: Point[];
diff --git a/src/app/statistics-page/statistics-table/statistics-table.component.html b/src/app/statistics-page/statistics-table/statistics-table.component.html
index 4a0f87076c..0cc4f5bfbc 100644
--- a/src/app/statistics-page/statistics-table/statistics-table.component.html
+++ b/src/app/statistics-page/statistics-table/statistics-table.component.html
@@ -11,7 +11,7 @@
@for (header of headers; track header) {
}
@@ -19,7 +19,7 @@
- {{ getLabel(point) | async }}
+ {{ point.label }}
@for (header of headers; track header) {
{
expect(de.query(By.css('table'))).toBeTruthy();
expect(de.query(By.css('th.views-header')).nativeElement.innerText)
- .toEqual('views');
+ .toEqual('statistics.table.header.views');
expect(de.query(By.css('th.downloads-header')).nativeElement.innerText)
- .toEqual('downloads');
+ .toEqual('statistics.table.header.downloads');
expect(de.query(By.css('td.item_1-views-data')).nativeElement.innerText)
.toEqual('7');
diff --git a/src/app/statistics-page/statistics-table/statistics-table.component.ts b/src/app/statistics-page/statistics-table/statistics-table.component.ts
index 9f59e33fa8..d3cf80330e 100644
--- a/src/app/statistics-page/statistics-table/statistics-table.component.ts
+++ b/src/app/statistics-page/statistics-table/statistics-table.component.ts
@@ -4,27 +4,11 @@ import {
Input,
OnInit,
} from '@angular/core';
-import {
- TranslateModule,
- TranslateService,
-} from '@ngx-translate/core';
-import {
- Observable,
- of,
-} from 'rxjs';
-import { map } from 'rxjs/operators';
+import { TranslateModule } from '@ngx-translate/core';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
-import {
- getFinishedRemoteData,
- getRemoteDataPayload,
-} from '../../core/shared/operators';
-import {
- Point,
- UsageReport,
-} from '../../core/statistics/models/usage-report.model';
-import { isEmpty } from '../../shared/empty.util';
+import { UsageReport } from '../../core/statistics/models/usage-report.model';
/**
* Component representing a statistics table for a given usage report.
@@ -57,7 +41,6 @@ export class StatisticsTableComponent implements OnInit {
constructor(
protected dsoService: DSpaceObjectDataService,
protected nameService: DSONameService,
- private translateService: TranslateService,
) {
}
@@ -68,23 +51,4 @@ export class StatisticsTableComponent implements OnInit {
this.headers = Object.keys(this.report.points[0].values);
}
}
-
- /**
- * Get the row label to display for a statistics point.
- * @param point the statistics point to get the label for
- */
- getLabel(point: Point): Observable {
- switch (this.report.reportType) {
- case 'TotalVisits':
- return this.dsoService.findById(point.id).pipe(
- getFinishedRemoteData(),
- getRemoteDataPayload(),
- map((item) => !isEmpty(item) ? this.nameService.getName(item) : this.translateService.instant('statistics.table.no-name')),
- );
- case 'TopCities':
- case 'topCountries':
- default:
- return of(point.label);
- }
- }
}
From 6232d4e9cf89b4fe42b7a975d6ac7d2050908fd4 Mon Sep 17 00:00:00 2001
From: Jens Vannerum
Date: Tue, 29 Apr 2025 12:20:25 +0200
Subject: [PATCH 37/54] 119612: fix spec test
---
.../search-export-csv/search-export-csv.component.spec.ts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts b/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts
index 9abdd8d366..f067263712 100644
--- a/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts
+++ b/src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts
@@ -9,6 +9,7 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs';
+import { ConfigurationDataService } from '../../../core/data/configuration-data.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths';
@@ -31,6 +32,7 @@ describe('SearchExportCsvComponent', () => {
let authorizationDataService: AuthorizationDataService;
let notificationsService;
let router;
+ let configurationDataService: jasmine.SpyObj;
const process = Object.assign(new Process(), { processId: 5, scriptName: 'metadata-export-search' });
@@ -45,6 +47,10 @@ describe('SearchExportCsvComponent', () => {
],
});
+ configurationDataService = jasmine.createSpyObj('ConfigurationDataService', {
+ findByPropertyName: observableOf({ payload: { value: '500' } }),
+ });
+
function initBeforeEachAsync() {
scriptDataService = jasmine.createSpyObj('scriptDataService', {
scriptWithNameExistsAndCanExecute: observableOf(true),
@@ -64,6 +70,7 @@ describe('SearchExportCsvComponent', () => {
{ provide: AuthorizationDataService, useValue: authorizationDataService },
{ provide: NotificationsService, useValue: notificationsService },
{ provide: Router, useValue: router },
+ { provide: ConfigurationDataService, useValue: configurationDataService },
],
}).compileComponents();
}
From ad7aa36f88542dd3736c62388d7fc9921bd8c72c Mon Sep 17 00:00:00 2001
From: Andrea Barbasso <´andrea.barbasso@4science.com´>
Date: Tue, 29 Apr 2025 15:48:30 +0200
Subject: [PATCH 38/54] [DURACOM-226] fix submission footer wrapping on medium
screens
---
.../form/footer/submission-form-footer.component.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/app/submission/form/footer/submission-form-footer.component.html b/src/app/submission/form/footer/submission-form-footer.component.html
index 1001d4c619..671888e824 100644
--- a/src/app/submission/form/footer/submission-form-footer.component.html
+++ b/src/app/submission/form/footer/submission-form-footer.component.html
@@ -1,6 +1,6 @@
@if (!!submissionId) {
-
-
+
+
@if ((showDepositAndDiscard | async)) {
}
-
+
@if ((hasUnsavedModification | async) !== true && (processingSaveStatus | async) !== true && (processingDepositStatus | async) !== true) {
{{'submission.general.info.saved' | translate}}
From 00143d29e7b2712b1b345c3dec614cd907bf46ea Mon Sep 17 00:00:00 2001
From: Andrea Barbasso <´andrea.barbasso@4science.com´>
Date: Tue, 29 Apr 2025 12:26:48 +0200
Subject: [PATCH 39/54] [DURACOM-354] fix missing input fields for integer
script parameters
---
.../integer-value-input.component.html | 11 +++
.../integer-value-input.component.scss | 5 ++
.../integer-value-input.component.spec.ts | 81 +++++++++++++++++++
.../integer-value-input.component.ts | 49 +++++++++++
.../parameter-value-input.component.html | 3 +
.../parameter-value-input.component.ts | 3 +-
.../scripts/script-parameter-type.model.ts | 1 +
src/assets/i18n/ar.json5 | 5 +-
src/assets/i18n/bn.json5 | 5 +-
src/assets/i18n/ca.json5 | 5 +-
src/assets/i18n/cs.json5 | 3 +
src/assets/i18n/de.json5 | 5 +-
src/assets/i18n/el.json5 | 5 +-
src/assets/i18n/en.json5 | 2 +
src/assets/i18n/es.json5 | 6 +-
src/assets/i18n/fi.json5 | 5 +-
src/assets/i18n/fr.json5 | 5 +-
src/assets/i18n/gd.json5 | 5 +-
src/assets/i18n/hi.json5 | 5 +-
src/assets/i18n/hu.json5 | 5 +-
src/assets/i18n/it.json5 | 5 +-
src/assets/i18n/ja.json5 | 6 +-
src/assets/i18n/kk.json5 | 5 +-
src/assets/i18n/lv.json5 | 6 +-
src/assets/i18n/nl.json5 | 6 +-
src/assets/i18n/pl.json5 | 3 +
src/assets/i18n/pt-BR.json5 | 5 +-
src/assets/i18n/pt-PT.json5 | 5 +-
src/assets/i18n/sr-cyr.json5 | 5 +-
src/assets/i18n/sr-lat.json5 | 5 +-
src/assets/i18n/sv.json5 | 6 +-
src/assets/i18n/sw.json5 | 6 +-
src/assets/i18n/tr.json5 | 5 +-
src/assets/i18n/uk.json5 | 3 +
src/assets/i18n/vi.json5 | 5 +-
35 files changed, 265 insertions(+), 25 deletions(-)
create mode 100644 src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.html
create mode 100644 src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.scss
create mode 100644 src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.spec.ts
create mode 100644 src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.ts
diff --git a/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.html b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.html
new file mode 100644
index 0000000000..f5861500f7
--- /dev/null
+++ b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.html
@@ -0,0 +1,11 @@
+
+@if (integer.invalid && (integer.dirty || integer.touched)) {
+
+ @if (integer.errors.required) {
+
+ {{'process.new.parameter.integer.required' | translate}}
+
+ }
+
+}
diff --git a/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.scss b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.scss
new file mode 100644
index 0000000000..8c6325f95a
--- /dev/null
+++ b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.scss
@@ -0,0 +1,5 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ gap: calc(var(--bs-spacer) / 2);
+}
diff --git a/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.spec.ts b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.spec.ts
new file mode 100644
index 0000000000..4c1fd4cd7a
--- /dev/null
+++ b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.spec.ts
@@ -0,0 +1,81 @@
+import {
+ ComponentFixture,
+ fakeAsync,
+ TestBed,
+ tick,
+ waitForAsync,
+} from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+import { By } from '@angular/platform-browser';
+import {
+ TranslateLoader,
+ TranslateModule,
+} from '@ngx-translate/core';
+
+import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
+import { IntegerValueInputComponent } from './integer-value-input.component';
+
+describe('IntegerValueInputComponent', () => {
+ let component: IntegerValueInputComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ FormsModule,
+ TranslateModule.forRoot({
+ loader: {
+ provide: TranslateLoader,
+ useClass: TranslateLoaderMock,
+ },
+ }),
+ IntegerValueInputComponent,
+ ],
+ providers: [],
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(IntegerValueInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should not show a validation error if the input field was left untouched but left empty', () => {
+ const validationError = fixture.debugElement.query(By.css('.validation-error'));
+ expect(validationError).toBeFalsy();
+ });
+
+ it('should show a validation error if the input field was touched but left empty', fakeAsync(() => {
+ component.value = undefined;
+ fixture.detectChanges();
+ tick();
+
+ const input = fixture.debugElement.query(By.css('input'));
+ input.triggerEventHandler('blur', null);
+
+ fixture.detectChanges();
+
+ const validationError = fixture.debugElement.query(By.css('.validation-error'));
+ expect(validationError).toBeTruthy();
+ }));
+
+ it('should not show a validation error if the input field was touched but not left empty', fakeAsync(() => {
+ component.value = 1;
+ fixture.detectChanges();
+ tick();
+
+ const input = fixture.debugElement.query(By.css('input'));
+ input.triggerEventHandler('blur', null);
+
+ fixture.detectChanges();
+
+ const validationError = fixture.debugElement.query(By.css('.validation-error'));
+ expect(validationError).toBeFalsy();
+ }));
+});
diff --git a/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.ts b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.ts
new file mode 100644
index 0000000000..0ae56c7062
--- /dev/null
+++ b/src/app/process-page/form/process-parameters/parameter-value-input/number-value-input/integer-value-input.component.ts
@@ -0,0 +1,49 @@
+import {
+ Component,
+ Input,
+ OnInit,
+ Optional,
+} from '@angular/core';
+import {
+ ControlContainer,
+ FormsModule,
+ NgForm,
+} from '@angular/forms';
+import { TranslateModule } from '@ngx-translate/core';
+
+import { controlContainerFactory } from '../../../process-form-factory';
+import { ValueInputComponent } from '../value-input.component';
+
+/**
+ * Represents the user-inputted value of an integer parameter
+ */
+@Component({
+ selector: 'ds-integer-value-input',
+ templateUrl: './integer-value-input.component.html',
+ styleUrls: ['./integer-value-input.component.scss'],
+ viewProviders: [{ provide: ControlContainer,
+ useFactory: controlContainerFactory,
+ deps: [[new Optional(), NgForm]] }],
+ standalone: true,
+ imports: [FormsModule, TranslateModule],
+})
+export class IntegerValueInputComponent extends ValueInputComponent implements OnInit {
+ /**
+ * The current value of the integer
+ */
+ value: number;
+
+ /**
+ * Initial value of the field
+ */
+ @Input() initialValue;
+
+ ngOnInit(): void {
+ this.value = this.initialValue;
+ }
+
+ setValue(value) {
+ this.value = value;
+ this.updateValue.emit(value);
+ }
+}
diff --git a/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.html b/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.html
index aa2425562e..ceb3e287ec 100644
--- a/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.html
+++ b/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.html
@@ -3,6 +3,9 @@
@case (parameterTypes.STRING) {
}
+ @case (parameterTypes.INTEGER) {
+
+ }
@case (parameterTypes.OUTPUT) {
}
diff --git a/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.ts b/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.ts
index 45d069c9ba..32c8355b9a 100644
--- a/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.ts
+++ b/src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.ts
@@ -17,6 +17,7 @@ import { controlContainerFactory } from '../../process-form-factory';
import { BooleanValueInputComponent } from './boolean-value-input/boolean-value-input.component';
import { DateValueInputComponent } from './date-value-input/date-value-input.component';
import { FileValueInputComponent } from './file-value-input/file-value-input.component';
+import { IntegerValueInputComponent } from './number-value-input/integer-value-input.component';
import { StringValueInputComponent } from './string-value-input/string-value-input.component';
/**
@@ -30,7 +31,7 @@ import { StringValueInputComponent } from './string-value-input/string-value-inp
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]] }],
standalone: true,
- imports: [StringValueInputComponent, DateValueInputComponent, FileValueInputComponent, BooleanValueInputComponent],
+ imports: [StringValueInputComponent, DateValueInputComponent, FileValueInputComponent, BooleanValueInputComponent, IntegerValueInputComponent],
})
export class ParameterValueInputComponent {
@Input() index: number;
diff --git a/src/app/process-page/scripts/script-parameter-type.model.ts b/src/app/process-page/scripts/script-parameter-type.model.ts
index 2b9fd358bf..a84bf50fad 100644
--- a/src/app/process-page/scripts/script-parameter-type.model.ts
+++ b/src/app/process-page/scripts/script-parameter-type.model.ts
@@ -3,6 +3,7 @@
*/
export enum ScriptParameterType {
STRING = 'String',
+ INTEGER = 'Integer',
DATE = 'date',
BOOLEAN = 'boolean',
FILE = 'InputStream',
diff --git a/src/assets/i18n/ar.json5 b/src/assets/i18n/ar.json5
index ed4d63273a..707cca2b93 100644
--- a/src/assets/i18n/ar.json5
+++ b/src/assets/i18n/ar.json5
@@ -6045,6 +6045,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "يرجى تحديد ملف",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "قيمة المتغير مطلوبة",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "قيمة المتغير مطلوبة",
@@ -11456,4 +11459,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/bn.json5 b/src/assets/i18n/bn.json5
index 8a16413aa2..2301a16a17 100644
--- a/src/assets/i18n/bn.json5
+++ b/src/assets/i18n/bn.json5
@@ -6525,6 +6525,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "দয়াকরে একটি ফাইল নির্বাচন করুন",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "পরামিতির মান প্রয়োজন",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "পরামিতির মান প্রয়োজন",
@@ -12379,4 +12382,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/ca.json5 b/src/assets/i18n/ca.json5
index 43d2c78098..4feb4591d6 100644
--- a/src/assets/i18n/ca.json5
+++ b/src/assets/i18n/ca.json5
@@ -5850,6 +5850,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Si us plau seleccioneu un fitxer",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Es requereix el valor del paràmetre",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Es requereix el valor del paràmetre",
@@ -10771,4 +10774,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5
index f5a71fe2b4..9d0b1d6e5b 100644
--- a/src/assets/i18n/cs.json5
+++ b/src/assets/i18n/cs.json5
@@ -6223,6 +6223,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Vyberte prosím soubor",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Hodnota parametru je povinná",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Hodnota parametru je povinná",
diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5
index e21f994390..e2647feeaa 100644
--- a/src/assets/i18n/de.json5
+++ b/src/assets/i18n/de.json5
@@ -5861,6 +5861,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Bitte wählen Sie eine Datei aus",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Parameterwert ist erforderlich",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Parameterwert ist erforderlich",
@@ -10792,4 +10795,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/el.json5 b/src/assets/i18n/el.json5
index 0bd1c119c5..02df140d61 100644
--- a/src/assets/i18n/el.json5
+++ b/src/assets/i18n/el.json5
@@ -6444,6 +6444,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Επιλέξτε ένα αρχείο",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Απαιτείται τιμή παραμέτρου",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Απαιτείται τιμή παραμέτρου",
@@ -12078,4 +12081,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5
index 56fd637ff3..98f7ffbeb4 100644
--- a/src/assets/i18n/en.json5
+++ b/src/assets/i18n/en.json5
@@ -3858,6 +3858,8 @@
"process.new.parameter.file.required": "Please select a file",
+ "process.new.parameter.integer.required": "Parameter value is required",
+
"process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.type.value": "value",
diff --git a/src/assets/i18n/es.json5 b/src/assets/i18n/es.json5
index 53f81bc238..3e60192ed5 100644
--- a/src/assets/i18n/es.json5
+++ b/src/assets/i18n/es.json5
@@ -5848,6 +5848,10 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Por favor seleccione un archivo",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Se requiere el valor del parámetro",
+
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Se requiere el valor del parámetro",
@@ -10768,4 +10772,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/fi.json5 b/src/assets/i18n/fi.json5
index d86d7dbb6d..31a32208bf 100644
--- a/src/assets/i18n/fi.json5
+++ b/src/assets/i18n/fi.json5
@@ -6223,6 +6223,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Valitse tiedosto, ole hyvä",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Parametrin arvo on pakollinen tieto",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Parametrin arvo on pakollinen tieto",
@@ -11699,4 +11702,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5
index 206d4e978f..d1cb4c2c14 100644
--- a/src/assets/i18n/fr.json5
+++ b/src/assets/i18n/fr.json5
@@ -6281,6 +6281,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Veuillez sélectionner un fichier",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Veuillez spécifier un paramètre",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Veuillez spécifier un paramètre",
@@ -11414,4 +11417,4 @@
"embargo.listelement.badge": "Embargo jusqu'à {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/gd.json5 b/src/assets/i18n/gd.json5
index a004acf557..07806e87cc 100644
--- a/src/assets/i18n/gd.json5
+++ b/src/assets/i18n/gd.json5
@@ -6588,6 +6588,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Tagh faidhle",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Feum air luach parameatair",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Feum air luach parameatair",
@@ -12471,4 +12474,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/hi.json5 b/src/assets/i18n/hi.json5
index 078c79350a..0289f7ea28 100644
--- a/src/assets/i18n/hi.json5
+++ b/src/assets/i18n/hi.json5
@@ -6440,6 +6440,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "कृपया एक फ़ाइल चुनें",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "पैरामीटर मान आवश्यक है",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "पैरामीटर मान आवश्यक है",
@@ -12073,4 +12076,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/hu.json5 b/src/assets/i18n/hu.json5
index 6ae4e10e55..ae7d9cd08a 100644
--- a/src/assets/i18n/hu.json5
+++ b/src/assets/i18n/hu.json5
@@ -6667,6 +6667,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Kérem válasszon egy fájlt",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Szüséges egy paraméter érték",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Szüséges egy paraméter érték",
@@ -12624,4 +12627,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5
index 9adc0ef9a4..7fd6c74deb 100644
--- a/src/assets/i18n/it.json5
+++ b/src/assets/i18n/it.json5
@@ -6256,6 +6256,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Seleziona un file",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Il valore del parametro è obbligatorio",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Il valore del parametro è obbligatorio",
@@ -11749,4 +11752,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/ja.json5 b/src/assets/i18n/ja.json5
index e2ecc9b3c1..4ef4440a8e 100644
--- a/src/assets/i18n/ja.json5
+++ b/src/assets/i18n/ja.json5
@@ -7714,6 +7714,10 @@
// TODO New key - Add a translation
"process.new.parameter.file.required": "Please select a file",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ // TODO New key - Add a translation
+ "process.new.parameter.integer.required": "Parameter value is required",
+
// "process.new.parameter.string.required": "Parameter value is required",
// TODO New key - Add a translation
"process.new.parameter.string.required": "Parameter value is required",
@@ -14194,4 +14198,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5
index 16668aa04b..7ce1327f59 100644
--- a/src/assets/i18n/kk.json5
+++ b/src/assets/i18n/kk.json5
@@ -6444,6 +6444,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Файлды таңдаңыз",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Параметр мәні қажет",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Параметр мәні қажет",
@@ -12098,4 +12101,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/lv.json5 b/src/assets/i18n/lv.json5
index fa72f49452..016bec0edc 100644
--- a/src/assets/i18n/lv.json5
+++ b/src/assets/i18n/lv.json5
@@ -6958,6 +6958,10 @@
// TODO New key - Add a translation
"process.new.parameter.file.required": "Please select a file",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ // TODO New key - Add a translation
+ "process.new.parameter.integer.required": "Parameter value is required",
+
// "process.new.parameter.string.required": "Parameter value is required",
// TODO New key - Add a translation
"process.new.parameter.string.required": "Parameter value is required",
@@ -13161,4 +13165,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/nl.json5 b/src/assets/i18n/nl.json5
index aef66f9802..5bd188953f 100644
--- a/src/assets/i18n/nl.json5
+++ b/src/assets/i18n/nl.json5
@@ -7200,6 +7200,10 @@
// TODO New key - Add a translation
"process.new.parameter.file.required": "Please select a file",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ // TODO New key - Add a translation
+ "process.new.parameter.integer.required": "Parameter value is required",
+
// "process.new.parameter.string.required": "Parameter value is required",
// TODO New key - Add a translation
"process.new.parameter.string.required": "Parameter value is required",
@@ -13500,4 +13504,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/pl.json5 b/src/assets/i18n/pl.json5
index 65d9c6421e..2ed993c52b 100644
--- a/src/assets/i18n/pl.json5
+++ b/src/assets/i18n/pl.json5
@@ -5786,6 +5786,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Proszę wybrać plik",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Wartość parametru jest wymagana",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Wartość parametru jest wymagana",
diff --git a/src/assets/i18n/pt-BR.json5 b/src/assets/i18n/pt-BR.json5
index d31ca0c542..82468941fc 100644
--- a/src/assets/i18n/pt-BR.json5
+++ b/src/assets/i18n/pt-BR.json5
@@ -5869,6 +5869,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Por favor, selecione um arquivo",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Valor do parâmetro é obrigatório",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Valor do parâmetro é obrigatório",
@@ -10812,4 +10815,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/pt-PT.json5 b/src/assets/i18n/pt-PT.json5
index d88efc40bc..e3cf349b19 100644
--- a/src/assets/i18n/pt-PT.json5
+++ b/src/assets/i18n/pt-PT.json5
@@ -5904,6 +5904,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Selecione um ficheiro",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "É necessário um valor no parâmetro",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "É necessário um valor no parâmetro",
@@ -11130,4 +11133,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/sr-cyr.json5 b/src/assets/i18n/sr-cyr.json5
index 6f5c53ed3a..05e279b977 100644
--- a/src/assets/i18n/sr-cyr.json5
+++ b/src/assets/i18n/sr-cyr.json5
@@ -6255,6 +6255,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Молимо изаберите фајл",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Вредност параметра је обавезна",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Вредност параметра је обавезна",
@@ -11719,4 +11722,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/sr-lat.json5 b/src/assets/i18n/sr-lat.json5
index 021053e524..fe661b5174 100644
--- a/src/assets/i18n/sr-lat.json5
+++ b/src/assets/i18n/sr-lat.json5
@@ -6253,6 +6253,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Molimo izaberite fajl",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Vrednost parametra je obavezna",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Vrednost parametra je obavezna",
@@ -11716,4 +11719,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/sv.json5 b/src/assets/i18n/sv.json5
index 666071fd53..d5d8e23b23 100644
--- a/src/assets/i18n/sv.json5
+++ b/src/assets/i18n/sv.json5
@@ -6596,6 +6596,10 @@
// TODO New key - Add a translation
"process.new.parameter.file.required": "Please select a file",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ // TODO New key - Add a translation
+ "process.new.parameter.integer.required": "Parameter value is required",
+
// "process.new.parameter.string.required": "Parameter value is required",
// TODO New key - Add a translation
"process.new.parameter.string.required": "Parameter value is required",
@@ -12527,4 +12531,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/sw.json5 b/src/assets/i18n/sw.json5
index e2ecc9b3c1..4ef4440a8e 100644
--- a/src/assets/i18n/sw.json5
+++ b/src/assets/i18n/sw.json5
@@ -7714,6 +7714,10 @@
// TODO New key - Add a translation
"process.new.parameter.file.required": "Please select a file",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ // TODO New key - Add a translation
+ "process.new.parameter.integer.required": "Parameter value is required",
+
// "process.new.parameter.string.required": "Parameter value is required",
// TODO New key - Add a translation
"process.new.parameter.string.required": "Parameter value is required",
@@ -14194,4 +14198,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/tr.json5 b/src/assets/i18n/tr.json5
index bdbc40cf9a..1fd622ea89 100644
--- a/src/assets/i18n/tr.json5
+++ b/src/assets/i18n/tr.json5
@@ -6740,6 +6740,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Lütfen dosya seçiniz.",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Parametre değeri gerekli",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Parametre değeri gerekli",
@@ -12721,4 +12724,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/uk.json5 b/src/assets/i18n/uk.json5
index 1a1eddf2c9..ae976e2bda 100644
--- a/src/assets/i18n/uk.json5
+++ b/src/assets/i18n/uk.json5
@@ -6768,6 +6768,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Виберіть файл",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Цей параметр є обв'язковим",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Цей параметр є обв'язковим",
diff --git a/src/assets/i18n/vi.json5 b/src/assets/i18n/vi.json5
index 3813297da8..83f7f2ad18 100644
--- a/src/assets/i18n/vi.json5
+++ b/src/assets/i18n/vi.json5
@@ -6344,6 +6344,9 @@
// "process.new.parameter.file.required": "Please select a file",
"process.new.parameter.file.required": "Vui lòng chọn một tệp tin",
+ // "process.new.parameter.integer.required": "Parameter value is required",
+ "process.new.parameter.integer.required": "Giá trị tham số là bắt buộc",
+
// "process.new.parameter.string.required": "Parameter value is required",
"process.new.parameter.string.required": "Giá trị tham số là bắt buộc",
@@ -11942,4 +11945,4 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
-}
\ No newline at end of file
+}
From 4c26359e293c995c4ec2cce78fd1a7e126dbcab0 Mon Sep 17 00:00:00 2001
From: guillermo2519
Date: Tue, 29 Apr 2025 21:24:59 -0600
Subject: [PATCH 40/54] Fixed Missing Tags in Import Popup from External
Sources - 4220
---
src/assets/i18n/en.json5 | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5
index 03c960ac1f..2709a1e99c 100644
--- a/src/assets/i18n/en.json5
+++ b/src/assets/i18n/en.json5
@@ -1,5 +1,4 @@
{
-
"401.help": "You're not authorized to access this page. You can use the button below to get back to the home page.",
"401.link.home-page": "Take me to the home page",
@@ -640,7 +639,6 @@
"admin.reports.items.title": "Title",
-
"admin.reports.commons.filters": "Filters",
"admin.reports.commons.additional-data": "Additional data to return",
@@ -661,7 +659,6 @@
"admin.reports.commons.filters.matches_all": "Matches all specified filters",
-
"admin.reports.commons.filters.property": "Item Property Filters",
"admin.reports.commons.filters.property.is_item": "Is Item - always true",
@@ -2910,6 +2907,14 @@
"item.preview.oaire.citation.endPage": "Citation end page",
+ "item.preview.dc.relation.hasversion": "Has version",
+
+ "item.preview.dc.relation.ispartofseries": "Is part of series",
+
+ "item.preview.dc.rights": "Rights",
+
+ "item.preview.dc.identifier.other": "Other Identifier",
+
"item.preview.dc.relation.issn": "ISSN",
"item.preview.dc.identifier.isbn": "ISBN",
@@ -5685,6 +5690,8 @@
"subscriptions.tooltip": "Subscribe",
+ "subscriptions.unsubscribe": "Unsubscribe",
+
"subscriptions.modal.title": "Subscriptions",
"subscriptions.modal.type-frequency": "Type and frequency",
@@ -5751,7 +5758,7 @@
"thumbnail.person.placeholder": "No Profile Picture Available",
- "title": "DSpace",
+ title: "DSpace",
"vocabulary-treeview.header": "Hierarchical tree view",
From 0aea4a7666a3f70d95d4bc7fd56798c401c57494 Mon Sep 17 00:00:00 2001
From: Zahraa Chreim
Date: Wed, 30 Apr 2025 10:22:01 +0200
Subject: [PATCH 41/54] 127047: Remove unnecessary CSS variable from spec setup
---
src/app/submission/edit/submission-edit.component.spec.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/app/submission/edit/submission-edit.component.spec.ts b/src/app/submission/edit/submission-edit.component.spec.ts
index 3ef6c7045d..29d872f542 100644
--- a/src/app/submission/edit/submission-edit.component.spec.ts
+++ b/src/app/submission/edit/submission-edit.component.spec.ts
@@ -57,8 +57,6 @@ describe('SubmissionEditComponent Component', () => {
const submissionObject: any = mockSubmissionObject;
beforeEach(waitForAsync(() => {
- // Fix for missing CSS custom property
- document.documentElement.style.setProperty('--bs-xl', '1200');
itemDataService = jasmine.createSpyObj('itemDataService', {
findByHref: createSuccessfulRemoteDataObject$(submissionObject.item),
});
From d3b48f4ea4290d5206ee881834e78cc06c3df144 Mon Sep 17 00:00:00 2001
From: Andreas Awouters
Date: Wed, 30 Apr 2025 13:42:51 +0200
Subject: [PATCH 42/54] 130484: Only add bundles when they are missing from the
subject
---
.../item-bitstreams/item-bitstreams.component.ts | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
index ea56a4274a..6fb2ae8c63 100644
--- a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
+++ b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
@@ -244,10 +244,24 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
),
map((bundlePage: PaginatedList) => bundlePage.page),
).subscribe((bundles: Bundle[]) => {
- this.bundlesSubject.next([...this.bundlesSubject.getValue(), ...bundles]);
+ this.updateBundlesSubject(bundles);
});
}
+ updateBundlesSubject(newBundles: Bundle[]) {
+ const currentBundles = this.bundlesSubject.getValue();
+ const bundlesToAdd: Bundle[] = [];
+
+ // Only add bundles to the bundle subject if they are not present yet
+ newBundles.forEach(newBundle => {
+ if (!currentBundles.some(currentBundle => currentBundle.id === newBundle.id)) {
+ bundlesToAdd.push(newBundle);
+ }
+ });
+
+ this.bundlesSubject.next([...currentBundles, ...bundlesToAdd]);
+ }
+
/**
* Submit the current changes
From 41afcf9cf653156c648815ee9bf77a986340b3d7 Mon Sep 17 00:00:00 2001
From: Andreas Awouters
Date: Wed, 30 Apr 2025 14:08:37 +0200
Subject: [PATCH 43/54] 130484: Correctly update the 'showLoadMoreLink$'
observable
---
.../item-bitstreams.component.ts | 22 ++++++++++---------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
index 6fb2ae8c63..40a75a59a5 100644
--- a/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
+++ b/src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
@@ -30,7 +30,6 @@ import {
map,
switchMap,
take,
- tap,
} from 'rxjs/operators';
import { AlertComponent } from 'src/app/shared/alert/alert.component';
import { AlertType } from 'src/app/shared/alert/alert-type';
@@ -239,27 +238,30 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
this.itemService.getBundles(this.item.id, new PaginatedSearchOptions({ pagination: this.bundlesOptions })).pipe(
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
- tap((bundlesPL: PaginatedList) =>
- this.showLoadMoreLink$.next(bundlesPL.pageInfo.currentPage < bundlesPL.pageInfo.totalPages),
- ),
- map((bundlePage: PaginatedList) => bundlePage.page),
- ).subscribe((bundles: Bundle[]) => {
- this.updateBundlesSubject(bundles);
+ ).subscribe((bundles: PaginatedList) => {
+ this.updateBundles(bundles);
});
}
- updateBundlesSubject(newBundles: Bundle[]) {
+ /**
+ * Update the subject containing the bundles with the provided bundles.
+ * Also updates the showLoadMoreLink observable so it does not show up when it is no longer necessary.
+ */
+ updateBundles(newBundlesPL: PaginatedList) {
const currentBundles = this.bundlesSubject.getValue();
const bundlesToAdd: Bundle[] = [];
// Only add bundles to the bundle subject if they are not present yet
- newBundles.forEach(newBundle => {
+ newBundlesPL.page.forEach(newBundle => {
if (!currentBundles.some(currentBundle => currentBundle.id === newBundle.id)) {
bundlesToAdd.push(newBundle);
}
});
- this.bundlesSubject.next([...currentBundles, ...bundlesToAdd]);
+ const updatedBundles = [...currentBundles, ...bundlesToAdd];
+
+ this.showLoadMoreLink$.next(updatedBundles.length < newBundlesPL.totalElements);
+ this.bundlesSubject.next(updatedBundles);
}
From 7b9cd73ee0811cd3188f21b4bbf5e1e64280849e Mon Sep 17 00:00:00 2001
From: FrancescoMolinaro
Date: Wed, 30 Apr 2025 14:51:42 +0200
Subject: [PATCH 44/54] [DURACOM-326] fix possible issue on missing value for
eperson patch
---
.../core/eperson/eperson-data.service.spec.ts | 32 +++++++++++++++
src/app/core/eperson/eperson-data.service.ts | 3 +-
src/app/shared/testing/eperson.mock.ts | 40 +++++++++++++++++++
3 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/src/app/core/eperson/eperson-data.service.spec.ts b/src/app/core/eperson/eperson-data.service.spec.ts
index ac735d8e92..f3209cd34c 100644
--- a/src/app/core/eperson/eperson-data.service.spec.ts
+++ b/src/app/core/eperson/eperson-data.service.spec.ts
@@ -31,6 +31,7 @@ import {
import {
EPersonMock,
EPersonMock2,
+ EPersonMockWithNoName,
} from '../../shared/testing/eperson.mock';
import { GroupMock } from '../../shared/testing/group-mock';
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
@@ -281,6 +282,37 @@ describe('EPersonDataService', () => {
});
});
+ describe('updateEPerson with non existing metadata', () => {
+ beforeEach(() => {
+ spyOn(service, 'findByHref').and.returnValue(createSuccessfulRemoteDataObject$(EPersonMockWithNoName));
+ });
+ describe('add name that was not previously set', () => {
+ beforeEach(() => {
+ const changedEPerson = Object.assign(new EPerson(), {
+ id: EPersonMock.id,
+ metadata: Object.assign(EPersonMock.metadata, {
+ 'eperson.firstname': [
+ {
+ language: null,
+ value: 'User',
+ },
+ ],
+ }),
+ email: EPersonMock.email,
+ canLogIn: EPersonMock.canLogIn,
+ requireCertificate: EPersonMock.requireCertificate,
+ _links: EPersonMock._links,
+ });
+ service.updateEPerson(changedEPerson).subscribe();
+ });
+ it('should send PatchRequest with add email operation', () => {
+ const operations = [{ op: 'add', path: '/eperson.firstname', value: [{ language: null, value: 'User' }] }];
+ const expected = new PatchRequest(requestService.generateRequestId(), epersonsEndpoint + '/' + EPersonMock.uuid, operations);
+ expect(requestService.send).toHaveBeenCalledWith(expected);
+ });
+ });
+ });
+
describe('clearEPersonRequests', () => {
beforeEach(() => {
spyOn(halService, 'getEndpoint').and.callFake((linkPath: string) => {
diff --git a/src/app/core/eperson/eperson-data.service.ts b/src/app/core/eperson/eperson-data.service.ts
index 33a02de4d7..a493f55405 100644
--- a/src/app/core/eperson/eperson-data.service.ts
+++ b/src/app/core/eperson/eperson-data.service.ts
@@ -269,7 +269,8 @@ export class EPersonDataService extends IdentifiableDataService impleme
* @param newEPerson
*/
private generateOperations(oldEPerson: EPerson, newEPerson: EPerson): Operation[] {
- let operations = this.comparator.diff(oldEPerson, newEPerson).filter((operation: Operation) => operation.op === 'replace');
+ let operations = this.comparator.diff(oldEPerson, newEPerson)
+ .filter((operation: Operation) => ['replace', 'add'].includes(operation.op));
if (hasValue(oldEPerson.email) && oldEPerson.email !== newEPerson.email) {
operations = [...operations, {
op: 'replace', path: '/email', value: newEPerson.email,
diff --git a/src/app/shared/testing/eperson.mock.ts b/src/app/shared/testing/eperson.mock.ts
index 002ee9326f..36daad1e57 100644
--- a/src/app/shared/testing/eperson.mock.ts
+++ b/src/app/shared/testing/eperson.mock.ts
@@ -91,3 +91,43 @@ export const EPersonMock2: EPerson = Object.assign(new EPerson(), {
],
},
});
+
+export const EPersonMockWithNoName: EPerson = Object.assign(new EPerson(), {
+ handle: null,
+ groups: [],
+ netid: 'test@test.com',
+ lastActive: '2018-05-14T12:25:42.411+0000',
+ canLogIn: true,
+ email: 'test@test.com',
+ requireCertificate: false,
+ selfRegistered: false,
+ _links: {
+ self: {
+ href: 'https://rest.api/dspace-spring-rest/api/eperson/epersons/testid',
+ },
+ groups: { href: 'https://rest.api/dspace-spring-rest/api/eperson/epersons/testid/groups' },
+ },
+ id: 'testid',
+ uuid: 'testid',
+ type: 'eperson',
+ metadata: {
+ 'dc.title': [
+ {
+ language: null,
+ value: 'User Test',
+ },
+ ],
+ 'eperson.lastname': [
+ {
+ language: null,
+ value: 'Test',
+ },
+ ],
+ 'eperson.language': [
+ {
+ language: null,
+ value: 'en',
+ },
+ ],
+ },
+});
From dc8b10593c1769e7476e54808317c3ea10945a87 Mon Sep 17 00:00:00 2001
From: Alexandre Vryghem
Date: Wed, 29 Jan 2025 20:53:54 +0100
Subject: [PATCH 45/54] 117287: Removed remaining observable function calls
from the HTML templates
---
.../metadata-field-form.component.html | 2 +-
.../metadata-field-form.component.ts | 127 +++++++++---------
.../item-authorizations.component.html | 10 +-
.../item-authorizations.component.spec.ts | 10 +-
.../item-authorizations.component.ts | 46 ++-----
.../virtual-metadata.component.html | 10 +-
.../virtual-metadata.component.spec.ts | 6 +-
.../virtual-metadata.component.ts | 40 ++++--
.../orcid-auth/orcid-auth.component.html | 22 +--
.../orcid-auth/orcid-auth.component.ts | 61 +++------
.../orcid-queue/orcid-queue.component.html | 8 +-
.../orcid-queue/orcid-queue.component.ts | 12 +-
src/app/navbar/navbar.component.html | 2 +-
.../overview/process-overview.component.html | 8 +-
.../overview/process-overview.component.ts | 4 +-
...rofile-page-researcher-form.component.html | 10 +-
.../profile-page-researcher-form.component.ts | 20 +--
.../profile-page/profile-page.component.html | 2 +-
.../profile-page.component.spec.ts | 55 +-------
.../profile-page/profile-page.component.ts | 7 -
.../register-email-form.component.html | 6 +-
.../register-email-form.component.ts | 14 +-
.../eperson-group-list.component.html | 61 ++++-----
.../eperson-group-list.component.spec.ts | 64 +++------
.../eperson-group-list.component.ts | 79 +++--------
src/app/shared/host-window.service.ts | 4 +-
.../pagination/pagination.component.html | 2 +-
.../shared/pagination/pagination.component.ts | 8 +-
...submission-form-section-add.component.html | 6 +-
.../submission-form-section-add.component.ts | 6 +
.../form/submission-form.component.html | 6 +-
.../form/submission-form.component.spec.ts | 2 +-
.../form/submission-form.component.ts | 11 +-
...mission-section-cc-licenses.component.html | 38 +++---
...ubmission-section-cc-licenses.component.ts | 23 +++-
.../section-identifiers.component.html | 25 ++--
.../section-identifiers.component.ts | 28 +---
.../dspace/app/navbar/navbar.component.html | 2 +-
38 files changed, 332 insertions(+), 515 deletions(-)
diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.html b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.html
index 4e29fbb6e8..e03c8adf91 100644
--- a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.html
+++ b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.html
@@ -1,4 +1,4 @@
-
+
{{messagePrefix + '.create' | translate}}
diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
index 773e0600fb..77c5d56378 100644
--- a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
+++ b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
@@ -10,7 +10,7 @@ import { RegistryService } from '../../../../core/registry/registry.service';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { take } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
-import { combineLatest } from 'rxjs';
+import { Observable } from 'rxjs';
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
@@ -89,6 +89,8 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy {
*/
@Output() submitForm: EventEmitter = new EventEmitter();
+ activeMetadataField$: Observable;
+
constructor(public registryService: RegistryService,
private formBuilderService: FormBuilderService,
private translateService: TranslateService) {
@@ -97,70 +99,65 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy {
/**
* Initialize the component, setting up the necessary Models for the dynamic form
*/
- ngOnInit() {
- combineLatest([
- this.translateService.get(`${this.messagePrefix}.element`),
- this.translateService.get(`${this.messagePrefix}.qualifier`),
- this.translateService.get(`${this.messagePrefix}.scopenote`)
- ]).subscribe(([element, qualifier, scopenote]) => {
- this.element = new DynamicInputModel({
- id: 'element',
- label: element,
- name: 'element',
- validators: {
- required: null,
- pattern: '^[^. ,]*$',
- maxLength: 64,
- },
- required: true,
- errorMessages: {
- pattern: 'error.validation.metadata.element.invalid-pattern',
- maxLength: 'error.validation.metadata.element.max-length',
- },
- });
- this.qualifier = new DynamicInputModel({
- id: 'qualifier',
- label: qualifier,
- name: 'qualifier',
- validators: {
- pattern: '^[^. ,]*$',
- maxLength: 64,
- },
- required: false,
- errorMessages: {
- pattern: 'error.validation.metadata.qualifier.invalid-pattern',
- maxLength: 'error.validation.metadata.qualifier.max-length',
- },
- });
- this.scopeNote = new DynamicInputModel({
- id: 'scopeNote',
- label: scopenote,
- name: 'scopeNote',
- required: false,
- });
- this.formModel = [
- new DynamicFormGroupModel(
- {
- id: 'metadatadatafieldgroup',
- group:[this.element, this.qualifier, this.scopeNote]
- })
- ];
- this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
- this.registryService.getActiveMetadataField().subscribe((field: MetadataField): void => {
- if (field == null) {
- this.clearFields();
- } else {
- this.formGroup.patchValue({
- metadatadatafieldgroup: {
- element: field.element,
- qualifier: field.qualifier,
- scopeNote: field.scopeNote,
- },
- });
- this.element.disabled = true;
- this.qualifier.disabled = true;
- }
- });
+ ngOnInit(): void {
+ this.activeMetadataField$ = this.registryService.getActiveMetadataField();
+ this.element = new DynamicInputModel({
+ id: 'element',
+ label: this.translateService.instant(`${this.messagePrefix}.element`),
+ name: 'element',
+ validators: {
+ required: null,
+ pattern: '^[^. ,]*$',
+ maxLength: 64,
+ },
+ required: true,
+ errorMessages: {
+ pattern: 'error.validation.metadata.element.invalid-pattern',
+ maxLength: 'error.validation.metadata.element.max-length',
+ },
+ });
+ this.qualifier = new DynamicInputModel({
+ id: 'qualifier',
+ label: this.translateService.instant(`${this.messagePrefix}.qualifier`),
+ name: 'qualifier',
+ validators: {
+ pattern: '^[^. ,]*$',
+ maxLength: 64,
+ },
+ required: false,
+ errorMessages: {
+ pattern: 'error.validation.metadata.qualifier.invalid-pattern',
+ maxLength: 'error.validation.metadata.qualifier.max-length',
+ },
+ });
+ this.scopeNote = new DynamicInputModel({
+ id: 'scopeNote',
+ label: this.translateService.instant(`${this.messagePrefix}.scopenote`),
+ name: 'scopeNote',
+ required: false,
+ });
+ this.formModel = [
+ new DynamicFormGroupModel(
+ {
+ id: 'metadatadatafieldgroup',
+ group:[this.element, this.qualifier, this.scopeNote]
+ })
+ ];
+ this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
+ this.registryService.getActiveMetadataField().subscribe((field: MetadataField): void => {
+ if (field == null) {
+ this.clearFields();
+ } else {
+ this.formGroup.patchValue({
+ metadatadatafieldgroup: {
+ element: field.element,
+ qualifier: field.qualifier,
+ scopeNote: field.scopeNote,
+ },
+ });
+ this.element.disabled = true;
+ this.qualifier.disabled = true;
+ }
});
}
diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html
index 7f121081f2..7569ebd6e3 100644
--- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html
+++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.html
@@ -1,9 +1,9 @@
-
-
+
+
-
+
0">
@@ -16,7 +16,7 @@
+ *ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id).bitstreams | async)">
diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts
index 5d2afbaf4c..3269a91118 100644
--- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts
+++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts
@@ -147,17 +147,9 @@ describe('ItemAuthorizationsComponent test suite', () => {
}));
});
- it('should get the item UUID', () => {
-
- expect(comp.getItemUUID()).toBeObservable(cold('(a|)', {
- a: item.id
- }));
-
- });
-
it('should get the item\'s bundle', () => {
- expect(comp.getItemBundles()).toBeObservable(cold('a', {
+ expect(comp.bundles$).toBeObservable(cold('a', {
a: bundles
}));
diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts
index 635cf455b5..09b3fe728e 100644
--- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts
+++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts
@@ -4,7 +4,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
-import { catchError, filter, first, map, mergeMap, take } from 'rxjs/operators';
+import { catchError, filter, map, mergeMap, take } from 'rxjs/operators';
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
import {
@@ -17,6 +17,7 @@ import { LinkService } from '../../../core/cache/builders/link.service';
import { Bundle } from '../../../core/shared/bundle.model';
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
import { Bitstream } from '../../../core/shared/bitstream.model';
+import { AlertType } from '../../../shared/alert/aletr-type';
/**
* Interface for a bundle's bitstream map entry
@@ -52,7 +53,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
* The target editing item
* @type {Observable- }
*/
- private item$: Observable
- ;
+ item$: Observable
- ;
/**
* Array to track all subscriptions and unsubscribe them onDestroy
@@ -91,16 +92,13 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
*/
private bitstreamPageSize = 4;
- /**
- * Initialize instance variables
- *
- * @param {LinkService} linkService
- * @param {ActivatedRoute} route
- * @param nameService
- */
+ itemName$: Observable
;
+
+ readonly AlertType = AlertType;
+
constructor(
- private linkService: LinkService,
- private route: ActivatedRoute,
+ protected linkService: LinkService,
+ protected route: ActivatedRoute,
public nameService: DSONameService
) {
}
@@ -109,37 +107,19 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
* Initialize the component, setting up the bundle and bitstream within the item
*/
ngOnInit(): void {
- this.getBundlesPerItem();
+ this.getBundlesPerItem();
+ this.itemName$ = this.getItemName();
}
/**
- * Return the item's UUID
+ * Return the item's name
*/
- getItemUUID(): Observable {
- return this.item$.pipe(
- map((item: Item) => item.id),
- first((UUID: string) => isNotEmpty(UUID))
- );
- }
-
- /**
- * Return the item's name
- */
- getItemName(): Observable {
+ private getItemName(): Observable {
return this.item$.pipe(
map((item: Item) => this.nameService.getName(item))
);
}
- /**
- * Return all item's bundles
- *
- * @return an observable that emits all item's bundles
- */
- getItemBundles(): Observable {
- return this.bundles$.asObservable();
- }
-
/**
* Get all bundles per item
* and all the bitstreams per bundle
diff --git a/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html b/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html
index b83b93d8f1..02cd26f78e 100644
--- a/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html
+++ b/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html
@@ -5,9 +5,9 @@
-
-
+
@@ -15,9 +15,9 @@
-
+
-
+
{{metadata.metadataField}}
diff --git a/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.spec.ts b/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.spec.ts
index 9391df4755..761dfa7fa2 100644
--- a/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.spec.ts
+++ b/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.spec.ts
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of as observableOf } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core';
-import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { DebugElement, NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { VirtualMetadataComponent } from './virtual-metadata.component';
import { Item } from '../../../core/shared/item.model';
@@ -62,6 +62,10 @@ describe('VirtualMetadataComponent', () => {
comp.leftItem = item;
comp.rightItem = relatedItem;
comp.relationshipId = relationshipId;
+ comp.ngOnChanges({
+ leftItem: new SimpleChange(undefined, comp.leftItem, true),
+ rightItem: new SimpleChange(undefined, comp.rightItem, true),
+ });
fixture.detectChanges();
});
diff --git a/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.ts b/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.ts
index 6f8991dc73..921a8fd9ee 100644
--- a/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.ts
+++ b/src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.ts
@@ -1,9 +1,15 @@
-import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, Inject, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
import {Observable} from 'rxjs';
import {Item} from '../../../core/shared/item.model';
import {MetadataValue} from '../../../core/shared/metadata.models';
import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service';
import { APP_CONFIG, AppConfig } from '../../../../config/app-config.interface';
+import { hasValue } from '../../../shared/empty.util';
+
+interface ItemDTO {
+ item: Item;
+ isSelectedVirtualMetadata$: Observable
;
+}
@Component({
selector: 'ds-virtual-metadata',
@@ -14,7 +20,7 @@ import { APP_CONFIG, AppConfig } from '../../../../config/app-config.interface';
* The component is shown when a relationship is marked to be deleted.
* Each item has a checkbox to indicate whether its virtual metadata should be saved as real metadata.
*/
-export class VirtualMetadataComponent implements OnInit {
+export class VirtualMetadataComponent implements OnInit, OnChanges {
/**
* The current url of this page
@@ -53,11 +59,9 @@ export class VirtualMetadataComponent implements OnInit {
showThumbnails: boolean;
/**
- * Get an array of the left and the right item of the relationship to be deleted.
+ * Array of the left and the right item of the relationship to be deleted.
*/
- get items() {
- return [this.leftItem, this.rightItem];
- }
+ itemDTOs: [ItemDTO, ItemDTO] = [undefined, undefined];
public virtualMetadata: Map = new Map();
@@ -109,14 +113,28 @@ export class VirtualMetadataComponent implements OnInit {
/**
* Prevent unnecessary rerendering so fields don't lose focus
*/
- trackItem(index, item: Item) {
- return item && item.uuid;
+ trackItem(index, itemDTO: ItemDTO) {
+ return itemDTO?.item?.uuid;
}
ngOnInit(): void {
- this.items.forEach((item) => {
- this.virtualMetadata.set(item.uuid, this.getVirtualMetadata(item));
- });
+ this.virtualMetadata.set(this.leftItem.uuid, this.getVirtualMetadata(this.leftItem));
+ this.virtualMetadata.set(this.rightItem.uuid, this.getVirtualMetadata(this.rightItem));
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if ((hasValue(changes.leftItem) || hasValue(changes.rightItem)) && hasValue(this.leftItem) && hasValue(this.rightItem)) {
+ this.itemDTOs = [
+ {
+ item: this.leftItem,
+ isSelectedVirtualMetadata$: this.isSelectedVirtualMetadataItem(this.leftItem),
+ },
+ {
+ item: this.rightItem,
+ isSelectedVirtualMetadata$: this.isSelectedVirtualMetadataItem(this.rightItem),
+ },
+ ];
+ }
}
}
diff --git a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html
index e57ce33008..6bbec3f47a 100644
--- a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html
+++ b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html
@@ -1,18 +1,18 @@
{{'person.orcid.registry.auth' | translate}}
-
+
-
+
-
+
{{getAuthorizationDescription(auth) | translate}}
@@ -25,13 +25,13 @@
-
+
{{'person.page.orcid.no-missing-authorizations-message' | translate}}
-
+
{{'person.page.orcid.missing-authorizations-message' | translate}}
-
+
{{getAuthorizationDescription(auth) | translate }}
@@ -41,11 +41,11 @@
-
+
{{ 'person.page.orcid.remove-orcid-message' | translate}}
-
+
@@ -54,7 +54,7 @@
{{'person.page.orcid.unlink.processing' | translate}}
-
{{ 'person.page.orcid.grant-authorizations' | translate }}
@@ -68,7 +68,7 @@
- {{ getOrcidNotLinkedMessage() | async }}
+ {{ getOrcidNotLinkedMessage() }}
diff --git a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts
index ea970e7d31..4fd77f9951 100644
--- a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts
+++ b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts
@@ -10,6 +10,7 @@ import { RemoteData } from '../../../core/data/remote-data';
import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { OrcidAuthService } from '../../../core/orcid/orcid-auth.service';
+import { AlertType } from '../../../shared/alert/aletr-type';
@Component({
selector: 'ds-orcid-auth',
@@ -26,43 +27,49 @@ export class OrcidAuthComponent implements OnInit, OnChanges {
/**
* The list of exposed orcid authorization scopes for the orcid profile
*/
- profileAuthorizationScopes: BehaviorSubject
= new BehaviorSubject([]);
+ profileAuthorizationScopes$: BehaviorSubject = new BehaviorSubject([]);
+
+ hasOrcidAuthorizations$: Observable;
/**
* The list of all orcid authorization scopes missing in the orcid profile
*/
- missingAuthorizationScopes: BehaviorSubject = new BehaviorSubject([]);
+ missingAuthorizationScopes: BehaviorSubject = new BehaviorSubject([]);
+
+ hasMissingOrcidAuthorizations$: Observable;
/**
* The list of all orcid authorization scopes available
*/
- orcidAuthorizationScopes: BehaviorSubject = new BehaviorSubject([]);
+ orcidAuthorizationScopes: BehaviorSubject = new BehaviorSubject([]);
/**
* A boolean representing if unlink operation is processing
*/
- unlinkProcessing: BehaviorSubject = new BehaviorSubject(false);
+ unlinkProcessing: BehaviorSubject = new BehaviorSubject(false);
/**
* A boolean representing if orcid profile is linked
*/
- private isOrcidLinked$: BehaviorSubject = new BehaviorSubject(false);
+ isOrcidLinked$: BehaviorSubject = new BehaviorSubject(false);
/**
* A boolean representing if only admin can disconnect orcid profile
*/
- private onlyAdminCanDisconnectProfileFromOrcid$: BehaviorSubject = new BehaviorSubject(false);
+ onlyAdminCanDisconnectProfileFromOrcid$: BehaviorSubject = new BehaviorSubject(false);
/**
* A boolean representing if owner can disconnect orcid profile
*/
- private ownerCanDisconnectProfileFromOrcid$: BehaviorSubject = new BehaviorSubject(false);
+ ownerCanDisconnectProfileFromOrcid$: BehaviorSubject = new BehaviorSubject(false);
/**
* An event emitted when orcid profile is unliked successfully
*/
@Output() unlink: EventEmitter = new EventEmitter();
+ readonly AlertType = AlertType;
+
constructor(
private orcidAuthService: OrcidAuthService,
private translateService: TranslateService,
@@ -76,6 +83,8 @@ export class OrcidAuthComponent implements OnInit, OnChanges {
this.orcidAuthorizationScopes.next(scopes);
this.initOrcidAuthSettings();
});
+ this.hasOrcidAuthorizations$ = this.hasOrcidAuthorizations();
+ this.hasMissingOrcidAuthorizations$ = this.hasMissingOrcidAuthorizations();
}
ngOnChanges(changes: SimpleChanges): void {
@@ -88,18 +97,11 @@ export class OrcidAuthComponent implements OnInit, OnChanges {
* Check if the list of exposed orcid authorization scopes for the orcid profile has values
*/
hasOrcidAuthorizations(): Observable {
- return this.profileAuthorizationScopes.asObservable().pipe(
+ return this.profileAuthorizationScopes$.pipe(
map((scopes: string[]) => scopes.length > 0)
);
}
- /**
- * Return the list of exposed orcid authorization scopes for the orcid profile
- */
- getOrcidAuthorizations(): Observable {
- return this.profileAuthorizationScopes.asObservable();
- }
-
/**
* Check if the list of exposed orcid authorization scopes for the orcid profile has values
*/
@@ -109,26 +111,12 @@ export class OrcidAuthComponent implements OnInit, OnChanges {
);
}
- /**
- * Return the list of exposed orcid authorization scopes for the orcid profile
- */
- getMissingOrcidAuthorizations(): Observable {
- return this.profileAuthorizationScopes.asObservable();
- }
-
- /**
- * Return a boolean representing if orcid profile is linked
- */
- isLinkedToOrcid(): Observable {
- return this.isOrcidLinked$.asObservable();
- }
-
- getOrcidNotLinkedMessage(): Observable {
+ getOrcidNotLinkedMessage(): string {
const orcid = this.item.firstMetadataValue('person.identifier.orcid');
if (orcid) {
- return this.translateService.get('person.page.orcid.orcid-not-linked-message', { 'orcid': orcid });
+ return this.translateService.instant('person.page.orcid.orcid-not-linked-message', { 'orcid': orcid });
} else {
- return this.translateService.get('person.page.orcid.no-orcid-message');
+ return this.translateService.instant('person.page.orcid.no-orcid-message');
}
}
@@ -141,13 +129,6 @@ export class OrcidAuthComponent implements OnInit, OnChanges {
return 'person.page.orcid.scope.' + scope.substring(1).replace('/', '-');
}
- /**
- * Return a boolean representing if only admin can disconnect orcid profile
- */
- onlyAdminCanDisconnectProfileFromOrcid(): Observable {
- return this.onlyAdminCanDisconnectProfileFromOrcid$.asObservable();
- }
-
/**
* Return a boolean representing if owner can disconnect orcid profile
*/
@@ -212,7 +193,7 @@ export class OrcidAuthComponent implements OnInit, OnChanges {
}
private setOrcidAuthorizationsFromItem(): void {
- this.profileAuthorizationScopes.next(this.orcidAuthService.getOrcidAuthorizationScopesByItem(this.item));
+ this.profileAuthorizationScopes$.next(this.orcidAuthService.getOrcidAuthorizationScopesByItem(this.item));
}
}
diff --git a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.html b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.html
index 6ba318f7fd..9b3e470fc1 100644
--- a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.html
+++ b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.html
@@ -3,13 +3,13 @@
{{ 'person.orcid.registry.queue' | translate }}
-
{{ 'person.page.orcid.sync-queue.empty-message' | translate}}
-
0"
+ 0"
[paginationOptions]="paginationOptions"
- [collectionSize]="(getList() | async)?.payload?.totalElements"
+ [collectionSize]="(list$ | async)?.payload?.totalElements"
[retainScrollPosition]="false" [hideGear]="true" (paginationChange)="updateList()">
@@ -22,7 +22,7 @@
-
+
diff --git a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts
index 6079287f71..098bac3fad 100644
--- a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts
+++ b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts
@@ -47,13 +47,12 @@ export class OrcidQueueComponent implements OnInit, OnDestroy {
/**
* A list of orcid queue records
*/
- private list$: BehaviorSubject>> = new BehaviorSubject>>({} as any);
+ list$: BehaviorSubject>> = new BehaviorSubject>>({} as any);
/**
* The AlertType enumeration
- * @type {AlertType}
*/
- AlertTypeEnum = AlertType;
+ readonly AlertTypeEnum = AlertType;
/**
* Array to track all subscriptions and unsubscribe them onDestroy
@@ -99,13 +98,6 @@ export class OrcidQueueComponent implements OnInit, OnDestroy {
);
}
- /**
- * Return the list of orcid queue records
- */
- getList(): Observable>> {
- return this.list$.asObservable();
- }
-
/**
* Return the icon class for the queue object type
*
diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html
index bc1e04f513..9b043a5865 100644
--- a/src/app/navbar/navbar.component.html
+++ b/src/app/navbar/navbar.component.html
@@ -1,4 +1,4 @@
-
diff --git a/src/app/process-page/overview/process-overview.component.html b/src/app/process-page/overview/process-overview.component.html
index 5bf5fee79f..288d6eb3ca 100644
--- a/src/app/process-page/overview/process-overview.component.html
+++ b/src/app/process-page/overview/process-overview.component.html
@@ -70,16 +70,16 @@
-
{{'process.overview.delete.body' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
-
+
{{'process.overview.delete.body' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
+
{{ 'process.overview.delete.processing' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
- {{'process.detail.delete.cancel' | translate}}
{{ 'process.overview.delete' | translate: {count: processBulkDeleteService.getAmountOfSelectedProcesses()} }}
diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts
index 7fa3b12dac..7d11962050 100644
--- a/src/app/process-page/overview/process-overview.component.ts
+++ b/src/app/process-page/overview/process-overview.component.ts
@@ -50,11 +50,12 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy {
*/
dateFormat = 'yyyy-MM-dd HH:mm:ss';
- processesToDelete: string[] = [];
private modalRef: any;
isProcessingSub: Subscription;
+ isProcessing$: Observable
;
+
constructor(protected processService: ProcessDataService,
protected paginationService: PaginationService,
protected ePersonService: EPersonDataService,
@@ -67,6 +68,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.setProcesses();
this.processBulkDeleteService.clearAllProcesses();
+ this.isProcessing$ = this.processBulkDeleteService.isProcessing$();
}
/**
diff --git a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html
index 2d959c1dfe..9e52b27af1 100644
--- a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html
+++ b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.html
@@ -13,12 +13,12 @@
{{'researcher.profile.not.associated' | translate}}
-
+
{{'researcher.profile.action.processing' | translate}}
-
+
{{'researcher.profile.create.new' | translate}}
@@ -27,10 +27,10 @@
{{'researcher.profile.view' | translate}}
-
+
{{'researcher.profile.action.processing' | translate}}
-
+
{{'researcher.profile.delete' | translate}}
diff --git a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts
index d63360fbf4..7797815d4f 100644
--- a/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts
+++ b/src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts
@@ -3,7 +3,7 @@ import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateService } from '@ngx-translate/core';
-import { BehaviorSubject, Observable } from 'rxjs';
+import { BehaviorSubject } from 'rxjs';
import { map, mergeMap, switchMap, take, tap } from 'rxjs/operators';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
@@ -157,24 +157,6 @@ export class ProfilePageResearcherFormComponent implements OnInit {
});
}
- /**
- * Return a boolean representing if a delete operation is pending.
- *
- * @return {Observable
}
- */
- isProcessingDelete(): Observable {
- return this.processingDelete$.asObservable();
- }
-
- /**
- * Return a boolean representing if a create operation is pending.
- *
- * @return {Observable}
- */
- isProcessingCreate(): Observable {
- return this.processingCreate$.asObservable();
- }
-
/**
* Create a new profile related to the current user from scratch.
*/
diff --git a/src/app/profile-page/profile-page.component.html b/src/app/profile-page/profile-page.component.html
index 44783da84e..329f5b2844 100644
--- a/src/app/profile-page/profile-page.component.html
+++ b/src/app/profile-page/profile-page.component.html
@@ -1,6 +1,6 @@
-
+
{{'profile.head' | translate}}
diff --git a/src/app/profile-page/profile-page.component.spec.ts b/src/app/profile-page/profile-page.component.spec.ts
index 709ed56790..2d7e799f68 100644
--- a/src/app/profile-page/profile-page.component.spec.ts
+++ b/src/app/profile-page/profile-page.component.spec.ts
@@ -11,14 +11,14 @@ import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model';
import { EPersonDataService } from '../core/eperson/eperson-data.service';
import { NotificationsService } from '../shared/notifications/notifications.service';
import { authReducer } from '../core/auth/auth.reducer';
-import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
+import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { createPaginatedList } from '../shared/testing/utils.test';
import { BehaviorSubject, of as observableOf } from 'rxjs';
import { AuthService } from '../core/auth/auth.service';
import { RestResponse } from '../core/cache/response.models';
import { provideMockStore } from '@ngrx/store/testing';
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
-import { cold, getTestScheduler } from 'jasmine-marbles';
+import { getTestScheduler } from 'jasmine-marbles';
import { By } from '@angular/platform-browser';
import { EmptySpecialGroupDataMock$, SpecialGroupDataMock$ } from '../shared/testing/special-group.mock';
import { ConfigurationDataService } from '../core/data/configuration-data.service';
@@ -309,55 +309,4 @@ describe('ProfilePageComponent', () => {
});
});
});
-
- describe('isResearcherProfileEnabled', () => {
-
- describe('when configuration service return values', () => {
-
- beforeEach(() => {
- configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(validConfiguration));
- fixture.detectChanges();
- });
-
- it('should return true', () => {
- const result = component.isResearcherProfileEnabled();
- const expected = cold('a', {
- a: true
- });
- expect(result).toBeObservable(expected);
- });
- });
-
- describe('when configuration service return no values', () => {
-
- beforeEach(() => {
- configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(emptyConfiguration));
- fixture.detectChanges();
- });
-
- it('should return false', () => {
- const result = component.isResearcherProfileEnabled();
- const expected = cold('a', {
- a: false
- });
- expect(result).toBeObservable(expected);
- });
- });
-
- describe('when configuration service return an error', () => {
-
- beforeEach(() => {
- configurationService.findByPropertyName.and.returnValue(createFailedRemoteDataObject$());
- fixture.detectChanges();
- });
-
- it('should return false', () => {
- const result = component.isResearcherProfileEnabled();
- const expected = cold('a', {
- a: false
- });
- expect(result).toBeObservable(expected);
- });
- });
- });
});
diff --git a/src/app/profile-page/profile-page.component.ts b/src/app/profile-page/profile-page.component.ts
index 343314999b..b537578dc7 100644
--- a/src/app/profile-page/profile-page.component.ts
+++ b/src/app/profile-page/profile-page.component.ts
@@ -192,11 +192,4 @@ export class ProfilePageComponent implements OnInit {
this.updateProfile();
}
- /**
- * Returns true if the researcher profile feature is enabled, false otherwise.
- */
- isResearcherProfileEnabled(): Observable
{
- return this.isResearcherProfileEnabled$.asObservable();
- }
-
}
diff --git a/src/app/register-email-form/register-email-form.component.html b/src/app/register-email-form/register-email-form.component.html
index ed79b1d2d1..190f5caa8d 100644
--- a/src/app/register-email-form/register-email-form.component.html
+++ b/src/app/register-email-form/register-email-form.component.html
@@ -41,13 +41,13 @@
{{ MESSAGE_PREFIX + '.google-recaptcha.open-cookie-settings' | translate }}
-
-
+
-
+
{{ MESSAGE_PREFIX + '.submit' | translate }}
diff --git a/src/app/register-email-form/register-email-form.component.ts b/src/app/register-email-form/register-email-form.component.ts
index ddb77b669c..de03ce2645 100644
--- a/src/app/register-email-form/register-email-form.component.ts
+++ b/src/app/register-email-form/register-email-form.component.ts
@@ -66,13 +66,9 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
subscriptions: Subscription[] = [];
- captchaVersion(): Observable {
- return this.googleRecaptchaService.captchaVersion();
- }
+ captchaVersion$: Observable;
- captchaMode(): Observable {
- return this.googleRecaptchaService.captchaMode();
- }
+ captchaMode$: Observable;
constructor(
private epersonRegistrationService: EpersonRegistrationService,
@@ -94,6 +90,8 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
}
ngOnInit(): void {
+ this.captchaVersion$ = this.googleRecaptchaService.captchaVersion();
+ this.captchaMode$ = this.googleRecaptchaService.captchaMode();
const validators: ValidatorFn[] = [
Validators.required,
Validators.email,
@@ -150,7 +148,7 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
register(tokenV2?) {
if (!this.form.invalid) {
if (this.registrationVerification) {
- this.subscriptions.push(combineLatest([this.captchaVersion(), this.captchaMode()]).pipe(
+ this.subscriptions.push(combineLatest([this.captchaVersion$, this.captchaMode$]).pipe(
switchMap(([captchaVersion, captchaMode]) => {
if (captchaVersion === 'v3') {
return this.googleRecaptchaService.getRecaptchaToken('register_email');
@@ -213,7 +211,7 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit {
*/
disableUntilCheckedFcn(): Observable {
const checked$ = this.checkboxCheckedSubject$.asObservable();
- return combineLatest([this.captchaVersion(), this.captchaMode(), checked$]).pipe(
+ return combineLatest([this.captchaVersion$, this.captchaMode$, checked$]).pipe(
// disable if checkbox is not checked or if reCaptcha is not in v2 checkbox mode
switchMap(([captchaVersion, captchaMode, checked]) => captchaVersion === 'v2' && captchaMode === 'checkbox' ? of(!checked) : of(false)),
startWith(true),
diff --git a/src/app/shared/eperson-group-list/eperson-group-list.component.html b/src/app/shared/eperson-group-list/eperson-group-list.component.html
index 6ee5aad943..0d4f2f4d1b 100644
--- a/src/app/shared/eperson-group-list/eperson-group-list.component.html
+++ b/src/app/shared/eperson-group-list/eperson-group-list.component.html
@@ -2,35 +2,36 @@
- 0"
- [paginationOptions]="paginationOptions"
- [collectionSize]="(getList() | async)?.payload?.totalElements"
- [retainScrollPosition]="true"
- [hideGear]="true">
+
+ 0"
+ [paginationOptions]="paginationOptions"
+ [collectionSize]="list.totalElements"
+ [retainScrollPosition]="true"
+ [hideGear]="true">
-
-
-
-
- {{'resource-policies.form.eperson-group-list.table.headers.id' | translate}}
- {{'resource-policies.form.eperson-group-list.table.headers.name' | translate}}
- {{'resource-policies.form.eperson-group-list.table.headers.action' | translate}}
-
-
-
-
- {{entry.id}}
- {{dsoNameService.getName(entry)}}
-
-
- {{'resource-policies.form.eperson-group-list.select.btn' | translate}}
-
-
-
-
-
-
-
-
+
+
+
+
+ {{ 'resource-policies.form.eperson-group-list.table.headers.id' | translate }}
+ {{ 'resource-policies.form.eperson-group-list.table.headers.name' | translate }}
+ {{ 'resource-policies.form.eperson-group-list.table.headers.action' | translate }}
+
+
+
+
+ {{ entry.id }}
+ {{ dsoNameService.getName(entry) }}
+
+
+ {{ 'resource-policies.form.eperson-group-list.select.btn' | translate }}
+
+
+
+
+
+
+
+
diff --git a/src/app/shared/eperson-group-list/eperson-group-list.component.spec.ts b/src/app/shared/eperson-group-list/eperson-group-list.component.spec.ts
index 8c5fc88137..f2ee4374a0 100644
--- a/src/app/shared/eperson-group-list/eperson-group-list.component.spec.ts
+++ b/src/app/shared/eperson-group-list/eperson-group-list.component.spec.ts
@@ -3,7 +3,7 @@ import { ChangeDetectorRef, Component, Injector, NO_ERRORS_SCHEMA } from '@angul
import { of as observableOf } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core';
-import { cold } from 'jasmine-marbles';
+import { hot } from 'jasmine-marbles';
import uniqueId from 'lodash/uniqueId';
import { createSuccessfulRemoteDataObject } from '../remote-data.utils';
@@ -22,14 +22,13 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { PaginationService } from '../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../testing/pagination-service.stub';
-describe('EpersonGroupListComponent test suite', () => {
+describe('EpersonGroupListComponent', () => {
let comp: EpersonGroupListComponent;
let compAsAny: any;
let fixture: ComponentFixture;
- let de;
let groupService: any;
let epersonService: any;
- let paginationService;
+ let paginationService: PaginationServiceStub;
const paginationOptions: PaginationComponentOptions = new PaginationComponentOptions();
paginationOptions.id = uniqueId('eperson-group-list-pagination-test');
@@ -91,7 +90,6 @@ describe('EpersonGroupListComponent test suite', () => {
}));
describe('', () => {
- let testComp: TestComponent;
let testFixture: ComponentFixture;
// synchronous beforeEach
@@ -101,7 +99,6 @@ describe('EpersonGroupListComponent test suite', () => {
`;
testFixture = createTestComponent(html, TestComponent) as ComponentFixture;
- testComp = testFixture.componentInstance;
});
afterEach(() => {
@@ -129,7 +126,6 @@ describe('EpersonGroupListComponent test suite', () => {
afterEach(() => {
comp = null;
compAsAny = null;
- de = null;
fixture.destroy();
});
@@ -147,16 +143,15 @@ describe('EpersonGroupListComponent test suite', () => {
fixture.detectChanges();
- expect(compAsAny.entrySelectedId.value).toBe(EPersonMock.id);
+ expect(comp.entrySelectedId$.value).toBe(EPersonMock.id);
});
it('should init the list of eperson', () => {
epersonService.searchByScope.and.returnValue(observableOf(epersonPaginatedListRD));
fixture.detectChanges();
- expect(compAsAny.list$.value).toEqual(epersonPaginatedListRD);
- expect(comp.getList()).toBeObservable(cold('a', {
- a: epersonPaginatedListRD
+ expect(comp.list$).toBeObservable(hot('(a|)', {
+ a: epersonPaginatedList,
}));
});
@@ -165,23 +160,13 @@ describe('EpersonGroupListComponent test suite', () => {
comp.emitSelect(EPersonMock);
expect(comp.select.emit).toHaveBeenCalled();
- expect(compAsAny.entrySelectedId.value).toBe(EPersonMock.id);
+ expect(comp.entrySelectedId$.value).toBe(EPersonMock.id);
});
- it('should return true when entry is selected', () => {
- compAsAny.entrySelectedId.next(EPersonMock.id);
+ it('should return the entrySelectedId$ value', () => {
+ comp.entrySelectedId$.next(EPersonMock.id);
- expect(comp.isSelected(EPersonMock)).toBeObservable(cold('a', {
- a: true
- }));
- });
-
- it('should return false when entry is not selected', () => {
- compAsAny.entrySelectedId.next('');
-
- expect(comp.isSelected(EPersonMock)).toBeObservable(cold('a', {
- a: false
- }));
+ expect(comp.entrySelectedId$.value).toBe(EPersonMock.id);
});
});
@@ -199,7 +184,6 @@ describe('EpersonGroupListComponent test suite', () => {
afterEach(() => {
comp = null;
compAsAny = null;
- de = null;
fixture.destroy();
});
@@ -217,16 +201,15 @@ describe('EpersonGroupListComponent test suite', () => {
fixture.detectChanges();
- expect(compAsAny.entrySelectedId.value).toBe(GroupMock.id);
+ expect(comp.entrySelectedId$.value).toBe(GroupMock.id);
});
it('should init the list of group', () => {
groupService.searchGroups.and.returnValue(observableOf(groupPaginatedListRD));
fixture.detectChanges();
- expect(compAsAny.list$.value).toEqual(groupPaginatedListRD);
- expect(comp.getList()).toBeObservable(cold('a', {
- a: groupPaginatedListRD
+ expect(comp.list$).toBeObservable(hot('(a|)', {
+ a: groupPaginatedList,
}));
});
@@ -235,27 +218,16 @@ describe('EpersonGroupListComponent test suite', () => {
comp.emitSelect(GroupMock);
expect(comp.select.emit).toHaveBeenCalled();
- expect(compAsAny.entrySelectedId.value).toBe(GroupMock.id);
+ expect(comp.entrySelectedId$.value).toBe(GroupMock.id);
});
- it('should return true when entry is selected', () => {
- compAsAny.entrySelectedId.next(EPersonMock.id);
+ it('should return the entrySelectedId$ value', () => {
+ comp.entrySelectedId$.next(GroupMock.id);
- expect(comp.isSelected(EPersonMock)).toBeObservable(cold('a', {
- a: true
- }));
- });
-
- it('should return false when entry is not selected', () => {
- compAsAny.entrySelectedId.next('');
-
- expect(comp.isSelected(EPersonMock)).toBeObservable(cold('a', {
- a: false
- }));
+ expect(comp.entrySelectedId$.value).toBe(GroupMock.id);
});
it('should update list on search triggered', () => {
- const options: PaginationComponentOptions = comp.paginationOptions;
const event: SearchEvent = {
scope: 'metadata',
query: 'test'
@@ -263,7 +235,7 @@ describe('EpersonGroupListComponent test suite', () => {
spyOn(comp, 'updateList');
comp.onSearch(event);
- expect(compAsAny.updateList).toHaveBeenCalledWith('metadata', 'test');
+ expect(comp.updateList).toHaveBeenCalledWith('metadata', 'test');
});
});
});
diff --git a/src/app/shared/eperson-group-list/eperson-group-list.component.ts b/src/app/shared/eperson-group-list/eperson-group-list.component.ts
index 7cad7a9783..ee8ead7f1d 100644
--- a/src/app/shared/eperson-group-list/eperson-group-list.component.ts
+++ b/src/app/shared/eperson-group-list/eperson-group-list.component.ts
@@ -1,14 +1,12 @@
import { Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output } from '@angular/core';
-import { BehaviorSubject, Observable, Subscription } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
import uniqueId from 'lodash/uniqueId';
-import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
-import { hasValue, isNotEmpty } from '../empty.util';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { EPERSON } from '../../core/eperson/models/eperson.resource-type';
import { GROUP } from '../../core/eperson/models/group.resource-type';
@@ -16,10 +14,12 @@ import { ResourceType } from '../../core/shared/resource-type';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../core/eperson/group-data.service';
import { fadeInOut } from '../animations/fade';
-import { getFirstCompletedRemoteData } from '../../core/shared/operators';
+import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../core/shared/operators';
import { PaginationService } from '../../core/pagination/pagination.service';
import { FindListOptions } from '../../core/data/find-list-options.model';
import { getDataServiceFor } from '../../core/data/base/data-service.decorator';
+import { EPerson } from '../../core/eperson/models/eperson.model';
+import { Group } from '../../core/eperson/models/group.model';
export interface SearchEvent {
scope: string;
@@ -79,21 +79,13 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
/**
* A list of eperson or group
*/
- private list$: BehaviorSubject>> = new BehaviorSubject>>({} as any);
+ list$: Observable>;
/**
* The eperson or group's id selected
* @type {string}
*/
- private entrySelectedId: BehaviorSubject = new BehaviorSubject('');
-
- /**
- * Array to track all subscriptions and unsubscribe them onDestroy
- * @type {Array}
- */
- private subs: Subscription[] = [];
-
- private pageConfigSub: Subscription;
+ entrySelectedId$: BehaviorSubject = new BehaviorSubject('');
/**
* Initialize instance variables and inject the properly DataService
@@ -119,7 +111,7 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
this.paginationOptions.pageSize = 5;
if (this.initSelected) {
- this.entrySelectedId.next(this.initSelected);
+ this.entrySelectedId$.next(this.initSelected);
}
this.updateList(this.currentSearchScope, this.currentSearchQuery);
@@ -133,28 +125,9 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
*/
emitSelect(entry: DSpaceObject): void {
this.select.emit(entry);
- this.entrySelectedId.next(entry.id);
+ this.entrySelectedId$.next(entry.id);
}
- /**
- * Return the list of eperson or group
- */
- getList(): Observable>> {
- return this.list$.asObservable();
- }
-
- /**
- * Return a boolean representing if a table row is selected
- *
- * @return {boolean}
- */
- isSelected(entry: DSpaceObject): Observable {
- return this.entrySelectedId.asObservable().pipe(
- map((selectedId) => isNotEmpty(selectedId) && selectedId === entry.id)
- );
- }
-
-
/**
* Method called on search
*/
@@ -169,38 +142,26 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
* Retrieve a paginate list of eperson or group
*/
updateList(scope: string, query: string): void {
- if (hasValue(this.pageConfigSub)) {
- this.pageConfigSub.unsubscribe();
- }
- this.pageConfigSub = this.paginationService.getCurrentPagination(this.paginationOptions.id, this.paginationOptions)
- .subscribe((paginationOptions) => {
- const options: FindListOptions = Object.assign({}, new FindListOptions(), {
+ this.list$ = this.paginationService.getCurrentPagination(this.paginationOptions.id, this.paginationOptions).pipe(
+ switchMap((paginationOptions) => {
+ const options: FindListOptions = Object.assign(new FindListOptions(), {
elementsPerPage: paginationOptions.pageSize,
- currentPage: paginationOptions.currentPage
- });
+ currentPage: paginationOptions.currentPage,
+ });
- const search$: Observable>> = this.isListOfEPerson ?
- (this.dataService as EPersonDataService).searchByScope(scope, query, options) :
- (this.dataService as GroupDataService).searchGroups(query, options);
-
- this.subs.push(search$.pipe(getFirstCompletedRemoteData())
- .subscribe((list: RemoteData>) => {
- if (hasValue(this.list$)) {
- this.list$.next(list);
- }
- })
+ return this.isListOfEPerson ?
+ (this.dataService as EPersonDataService).searchByScope(scope, query, options) :
+ (this.dataService as GroupDataService).searchGroups(query, options);
+ }),
+ getFirstCompletedRemoteData(),
+ getRemoteDataPayload(),
);
- });
}
/**
* Unsubscribe from all subscriptions
*/
ngOnDestroy(): void {
- this.list$ = null;
- this.subs
- .filter((subscription) => hasValue(subscription))
- .forEach((subscription) => subscription.unsubscribe());
this.paginationService.clearPagination(this.paginationOptions.id);
}
diff --git a/src/app/shared/host-window.service.ts b/src/app/shared/host-window.service.ts
index 6d13d921e0..2723eb86c0 100644
--- a/src/app/shared/host-window.service.ts
+++ b/src/app/shared/host-window.service.ts
@@ -100,10 +100,10 @@ export class HostWindowService {
}
isXsOrSm(): Observable {
- return observableCombineLatest(
+ return observableCombineLatest([
this.isXs(),
this.isSm()
- ).pipe(
+ ]).pipe(
map(([isXs, isSm]) => isXs || isSm),
distinctUntilChanged()
);
diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html
index d1a820b1da..ddd7f1fa99 100644
--- a/src/app/shared/pagination/pagination.component.html
+++ b/src/app/shared/pagination/pagination.component.html
@@ -21,7 +21,7 @@
-