Skip to content

Commit 6159c81

Browse files
committed
Merge tag 'dspace-cris-2024.02.02' into main-cris
Release DSpace-CRIS 2024.02.02
2 parents cad38c9 + c159bea commit 6159c81

7 files changed

Lines changed: 151 additions & 6 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dspace-angular",
3-
"version": "2024.02.02-SNAPSHOT",
3+
"version": "2024.02.02",
44
"scripts": {
55
"ng": "ng",
66
"config:watch": "nodemon",

src/app/process-page/form/scripts-select/scripts-select.component.spec.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,86 @@ describe('ScriptsSelectComponent', () => {
109109
const validationError = fixture.debugElement.query(By.css('.validation-error'));
110110
expect(validationError).toBeFalsy();
111111
}));
112+
113+
it('should load more scripts when scrolled to the bottom', fakeAsync(() => {
114+
spyOn(component, 'loadScripts');
115+
const event = {
116+
target: {
117+
scrollTop: 100,
118+
clientHeight: 200,
119+
scrollHeight: 300,
120+
},
121+
};
122+
123+
component.onScroll(event);
124+
tick();
125+
126+
expect(component.loadScripts).toHaveBeenCalled();
127+
}));
128+
129+
it('should load more scripts when scrolled almost to the bottom', fakeAsync(() => {
130+
spyOn(component, 'loadScripts');
131+
const event = {
132+
target: {
133+
scrollTop: 99,
134+
clientHeight: 200,
135+
scrollHeight: 300,
136+
},
137+
};
138+
139+
component.onScroll(event);
140+
tick();
141+
142+
expect(component.loadScripts).toHaveBeenCalled();
143+
}));
144+
145+
it('should not load more scripts if already loading', fakeAsync(() => {
146+
spyOn(component, 'loadScripts');
147+
component.isLoading$.next(true);
148+
const event = {
149+
target: {
150+
scrollTop: 100,
151+
clientHeight: 200,
152+
scrollHeight: 300,
153+
},
154+
};
155+
156+
component.onScroll(event);
157+
tick();
158+
159+
expect(component.loadScripts).not.toHaveBeenCalled();
160+
}));
161+
162+
it('should not load more scripts if it is the last page', fakeAsync(() => {
163+
spyOn(component, 'loadScripts');
164+
(component as any)._isLastPage = true;
165+
const event = {
166+
target: {
167+
scrollTop: 100,
168+
clientHeight: 200,
169+
scrollHeight: 300,
170+
},
171+
};
172+
173+
component.onScroll(event);
174+
tick();
175+
176+
expect(component.loadScripts).not.toHaveBeenCalled();
177+
}));
178+
179+
it('should not load more scripts if not scrolled to the bottom', fakeAsync(() => {
180+
spyOn(component, 'loadScripts');
181+
const event = {
182+
target: {
183+
scrollTop: 50,
184+
clientHeight: 200,
185+
scrollHeight: 300,
186+
},
187+
};
188+
189+
component.onScroll(event);
190+
tick();
191+
192+
expect(component.loadScripts).not.toHaveBeenCalled();
193+
}));
112194
});

src/app/process-page/form/scripts-select/scripts-select.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy {
128128
* @param event The scroll event
129129
*/
130130
onScroll(event: any) {
131-
if (event.target.scrollTop + event.target.clientHeight >= event.target.scrollHeight) {
131+
// offset to fix issues with zooming in or out in the browser
132+
const offset = 5;
133+
if (event.target.scrollTop + event.target.clientHeight + offset >= event.target.scrollHeight) {
132134
if (!this.isLoading$.value && !this._isLastPage) {
133135
this.scriptOptions.currentPage++;
134136
this.loadScripts();

src/app/process-page/overview/process-overview.service.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,26 @@ export class ProcessOverviewService {
9898
);
9999
}
100100

101+
/**
102+
* Retrieve processes by their owner and status
103+
* @param processStatus The status for which to retrieve processes
104+
* @param findListOptions The FindListOptions object
105+
* @param autoRefreshingIntervalInMs Optional: The interval by which to automatically refresh the retrieved processes.
106+
* Leave empty or set to null to only retrieve the processes once.
107+
*/
108+
getOwnProcessesByProcessStatus(processStatus: ProcessStatus, findListOptions?: FindListOptions, autoRefreshingIntervalInMs: number = null) {
109+
const requestParam = new RequestParam('processStatus', processStatus);
110+
const options: FindListOptions = Object.assign(new FindListOptions(), {
111+
searchParams: [requestParam],
112+
elementsPerPage: 5,
113+
}, findListOptions);
114+
115+
if (hasValue(autoRefreshingIntervalInMs) && autoRefreshingIntervalInMs > 0) {
116+
this.processDataService.stopAutoRefreshing(processStatus);
117+
return this.processDataService.autoRefreshingSearchBy(processStatus, 'own', options, autoRefreshingIntervalInMs);
118+
} else {
119+
return this.processDataService.searchBy('own', options);
120+
}
121+
}
122+
101123
}

src/app/process-page/overview/table/process-overview-table.component.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ import {
1111
TranslateModule,
1212
TranslateService,
1313
} from '@ngx-translate/core';
14-
import { BehaviorSubject } from 'rxjs';
14+
import {
15+
BehaviorSubject,
16+
of,
17+
} from 'rxjs';
1518
import { take } from 'rxjs/operators';
1619

1720
import { AuthService } from '../../../core/auth/auth.service';
21+
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
1822
import { ProcessDataService } from '../../../core/data/processes/process-data.service';
1923
import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
2024
import { EPerson } from '../../../core/eperson/models/eperson.model';
@@ -52,6 +56,10 @@ describe('ProcessOverviewTableComponent', () => {
5256

5357
let translateServiceSpy: jasmine.SpyObj<TranslateService>;
5458

59+
const authorizationService = jasmine.createSpyObj('authorizationService', {
60+
isAuthorized: of(true),
61+
});
62+
5563
function init() {
5664
processes = [
5765
Object.assign(new Process(), {
@@ -104,6 +112,7 @@ describe('ProcessOverviewTableComponent', () => {
104112
sort: 'creationTime',
105113
},
106114
getProcessesByProcessStatus: createSuccessfulRemoteDataObject$(createPaginatedList(processes)).pipe(take(1)),
115+
getOwnProcessesByProcessStatus: createSuccessfulRemoteDataObject$(createPaginatedList(processes)).pipe(take(1)),
107116
});
108117
processService = jasmine.createSpyObj('processService', {
109118
searchBy: createSuccessfulRemoteDataObject$(createPaginatedList(processes)).pipe(take(1)),
@@ -153,6 +162,7 @@ describe('ProcessOverviewTableComponent', () => {
153162
{ provide: NgbModal, useValue: modalService },
154163
{ provide: AuthService, useValue: authService },
155164
{ provide: RouteService, useValue: routeService },
165+
{ provide: AuthorizationDataService, useValue: authorizationService },
156166
],
157167
schemas: [NO_ERRORS_SCHEMA],
158168
}).overrideComponent(ProcessOverviewTableComponent, {
@@ -227,6 +237,7 @@ describe('ProcessOverviewTableComponent', () => {
227237
});
228238

229239
describe('getEPersonName function', () => {
240+
230241
it('should return unknown user when id is null', (done: DoneFn) => {
231242
const id = null;
232243
const expectedTranslation = 'process.overview.unknown.user';
@@ -264,4 +275,16 @@ describe('ProcessOverviewTableComponent', () => {
264275
expect(translateServiceSpy.get).not.toHaveBeenCalled();
265276
});
266277
});
278+
279+
describe('when user is not admin', () => {
280+
beforeAll(waitForAsync(() => {
281+
authorizationService.isAuthorized.and.callFake(() => of(false));
282+
}));
283+
284+
it ('should call getOwnProcessesByProcessStatus', () => {
285+
expect(processOverviewService.getOwnProcessesByProcessStatus).toHaveBeenCalled();
286+
});
287+
288+
});
289+
267290
});

src/app/process-page/overview/table/process-overview-table.component.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import { PaginationService } from 'src/app/core/pagination/pagination.service';
4040

4141
import { AuthService } from '../../../core/auth/auth.service';
4242
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
43+
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
44+
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
4345
import { FindListOptions } from '../../../core/data/find-list-options.model';
4446
import { PaginatedList } from '../../../core/data/paginated-list.model';
4547
import { RemoteData } from '../../../core/data/remote-data';
@@ -166,11 +168,13 @@ export class ProcessOverviewTableComponent implements OnInit, OnDestroy {
166168
protected router: Router,
167169
protected auth: AuthService,
168170
private translateService: TranslateService,
171+
protected authorizationService: AuthorizationDataService,
169172
@Inject(PLATFORM_ID) protected platformId: object,
170173
) {
171174
}
172175

173176
ngOnInit() {
177+
const isAdmin$ = this.isCurrentUserAdmin();
174178
// Only auto refresh on browsers
175179
if (!isPlatformBrowser(this.platformId)) {
176180
this.useAutoRefreshingSearchBy = false;
@@ -216,8 +220,16 @@ export class ProcessOverviewTableComponent implements OnInit, OnDestroy {
216220
this.processOverviewService.getFindListOptions(paginationOptions, this.sortField)),
217221
// Use the findListOptions to retrieve the relevant processes every interval
218222
switchMap((findListOptions: FindListOptions) =>
219-
this.processOverviewService.getProcessesByProcessStatus(
220-
this.processStatus, findListOptions, this.useAutoRefreshingSearchBy ? this.autoRefreshInterval : null),
223+
isAdmin$.pipe(
224+
switchMap((isAdmin: boolean) => {
225+
const autoRefreshInterval = this.useAutoRefreshingSearchBy ? this.autoRefreshInterval : null;
226+
if (isAdmin) {
227+
return this.processOverviewService.getProcessesByProcessStatus(this.processStatus, findListOptions, autoRefreshInterval);
228+
} else {
229+
return this.processOverviewService.getOwnProcessesByProcessStatus(this.processStatus, findListOptions, autoRefreshInterval);
230+
}
231+
}),
232+
),
221233
),
222234
// Redirect the user when he is logged out
223235
redirectOn4xx(this.router, this.auth),
@@ -310,4 +322,8 @@ export class ProcessOverviewTableComponent implements OnInit, OnDestroy {
310322
this.processOverviewService.stopAutoRefreshing(this.processStatus);
311323
}
312324

325+
isCurrentUserAdmin(): Observable<boolean> {
326+
return this.authorizationService.isAuthorized(FeatureID.AdministratorOf, undefined, undefined);
327+
}
328+
313329
}

src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
218218
node.item,
219219
level,
220220
node.hasChildren,
221-
((!node.isSearchNode && node.hasChildren) || (node.isSearchNode && node.hasChildren && isNotEmpty(node.children))),
221+
(node.hasChildren && isNotEmpty(node.children)),
222222
node.pageInfo,
223223
node.loadMoreParentItem,
224224
node.isSearchNode,

0 commit comments

Comments
 (0)