Skip to content

Commit b57a5f5

Browse files
author
Andrea Barbasso
committed
Merged in task/dspace-cris-2025_02_x/DSC-2709 (pull request DSpace#4074)
Task/dspace cris 2025 02 x/DSC-2709 Approved-by: Francesco Molinaro
2 parents 006d111 + 6dee25d commit b57a5f5

7 files changed

Lines changed: 272 additions & 139 deletions

src/app/app.menus.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { buildMenuStructure } from './shared/menu/menu.structure';
99
import { MenuID } from './shared/menu/menu-id.model';
1010
import { MenuRoute } from './shared/menu/menu-route.model';
1111
import { AccessControlMenuProvider } from './shared/menu/providers/access-control.menu';
12+
import { AdminCommunityListMenuProvider } from './shared/menu/providers/admin-community-list.menu';
1213
import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.menu';
1314
import { CoarNotifyMenuProvider } from './shared/menu/providers/coar-notify.menu';
1415
import { SubscribeMenuProvider } from './shared/menu/providers/comcol-subscribe.menu';
@@ -59,6 +60,7 @@ export const MENUS = buildMenuStructure({
5960
StatisticsMenuProvider,
6061
],
6162
[MenuID.ADMIN]: [
63+
AdminCommunityListMenuProvider,
6264
NewMenuProvider,
6365
EditMenuProvider,
6466
ImportMenuProvider,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
9+
import { TestBed } from '@angular/core/testing';
10+
import { of } from 'rxjs';
11+
12+
import { APP_CONFIG } from '../../../../config/app-config.interface';
13+
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
14+
import { AuthorizationDataServiceStub } from '../../testing/authorization-service.stub';
15+
import { MenuItemType } from '../menu-item-type.model';
16+
import { PartialMenuSection } from '../menu-provider.model';
17+
import { AdminCommunityListMenuProvider } from './admin-community-list.menu';
18+
19+
describe('AdminCommunityListMenuProvider', () => {
20+
const expectedSections: PartialMenuSection[] = [
21+
{
22+
visible: true,
23+
model: {
24+
type: MenuItemType.LINK,
25+
text: `menu.section.communities_and_collections`,
26+
link: `/community-list`,
27+
},
28+
icon: 'users',
29+
},
30+
];
31+
32+
let provider: AdminCommunityListMenuProvider;
33+
let authorizationServiceStub = new AuthorizationDataServiceStub();
34+
35+
beforeEach(() => {
36+
spyOn(authorizationServiceStub, 'isAuthorized').and.returnValue(
37+
of(true),
38+
);
39+
40+
TestBed.configureTestingModule({
41+
providers: [
42+
AdminCommunityListMenuProvider,
43+
{ provide: APP_CONFIG, useValue: { layout: { navbar: { showCommunityCollection: false } } } },
44+
{ provide: AuthorizationDataService, useValue: authorizationServiceStub } ],
45+
});
46+
provider = TestBed.inject(AdminCommunityListMenuProvider);
47+
});
48+
49+
it('should be created', () => {
50+
expect(provider).toBeTruthy();
51+
});
52+
53+
it('getSections should return expected menu sections', (done) => {
54+
provider.getSections().subscribe((sections) => {
55+
expect(sections).toEqual(expectedSections);
56+
done();
57+
});
58+
});
59+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
9+
import {
10+
inject,
11+
Injectable,
12+
} from '@angular/core';
13+
import {
14+
map,
15+
Observable,
16+
of,
17+
} from 'rxjs';
18+
19+
import { APP_CONFIG } from '../../../../config/app-config.interface';
20+
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
21+
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
22+
import { MenuItemType } from '../menu-item-type.model';
23+
import {
24+
AbstractMenuProvider,
25+
PartialMenuSection,
26+
} from '../menu-provider.model';
27+
28+
/**
29+
* Menu provider to create the "Communities & Collections" menu section in the admin navbar
30+
*/
31+
@Injectable()
32+
export class AdminCommunityListMenuProvider extends AbstractMenuProvider {
33+
34+
protected appConfig = inject(APP_CONFIG);
35+
protected authorizationService = inject(AuthorizationDataService);
36+
37+
public getSections(): Observable<PartialMenuSection[]> {
38+
if (this.appConfig.layout.navbar.showCommunityCollection) {
39+
return of([]);
40+
}
41+
return this.authorizationService.isAuthorized(FeatureID.IsComColAdmin).pipe(
42+
map(isComColAdmin => {
43+
return [{
44+
visible: isComColAdmin,
45+
model: {
46+
type: MenuItemType.LINK,
47+
text: `menu.section.communities_and_collections`,
48+
link: `/community-list`,
49+
},
50+
icon: 'users',
51+
},
52+
] as PartialMenuSection[];
53+
}));
54+
}
55+
}

src/app/shared/menu/providers/community-list.menu.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { TestBed } from '@angular/core/testing';
1010

11+
import { APP_CONFIG } from '../../../../config/app-config.interface';
1112
import { MenuItemType } from '../menu-item-type.model';
1213
import { PartialMenuSection } from '../menu-provider.model';
1314
import { CommunityListMenuProvider } from './community-list.menu';
@@ -31,6 +32,7 @@ describe('CommunityListMenuProvider', () => {
3132
TestBed.configureTestingModule({
3233
providers: [
3334
CommunityListMenuProvider,
35+
{ provide: APP_CONFIG, useValue: { layout: { navbar: { showCommunityCollection: true } } } },
3436
],
3537
});
3638
provider = TestBed.inject(CommunityListMenuProvider);

src/app/shared/menu/providers/community-list.menu.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
* http://www.dspace.org/license/
77
*/
88

9-
import { Injectable } from '@angular/core';
9+
import {
10+
inject,
11+
Injectable,
12+
} from '@angular/core';
1013
import {
1114
Observable,
1215
of,
1316
} from 'rxjs';
1417

18+
import { APP_CONFIG } from '../../../../config/app-config.interface';
1519
import { MenuItemType } from '../menu-item-type.model';
1620
import {
1721
AbstractMenuProvider,
@@ -23,7 +27,13 @@ import {
2327
*/
2428
@Injectable()
2529
export class CommunityListMenuProvider extends AbstractMenuProvider {
30+
31+
protected appConfig = inject(APP_CONFIG);
2632
public getSections(): Observable<PartialMenuSection[]> {
33+
if (!this.appConfig.layout.navbar.showCommunityCollection) {
34+
return of([]);
35+
}
36+
2737
return of([
2838
{
2939
visible: true,
Lines changed: 11 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,14 @@
11
import { TestBed } from '@angular/core/testing';
2+
import { ActivatedRoute } from '@angular/router';
23
import { TranslateModule } from '@ngx-translate/core';
34
import { of } from 'rxjs';
45

56
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
6-
import { Item } from '../../../core/shared/item.model';
7-
import { ITEM } from '../../../core/shared/item.resource-type';
8-
import { createSuccessfulRemoteDataObject } from '../../remote-data.utils';
9-
import { MenuItemType } from '../menu-item-type.model';
10-
import { PartialMenuSection } from '../menu-provider.model';
7+
import { ActivatedRouteStub } from '../../testing/active-router.stub';
118
import { StatisticsMenuProvider } from './statistics.menu';
129

1310
describe('StatisticsMenuProvider', () => {
14-
15-
const expectedSectionsNoDSO: PartialMenuSection[] = [
16-
{
17-
visible: true,
18-
model: {
19-
type: MenuItemType.LINK,
20-
text: 'menu.section.statistics',
21-
link: `statistics`,
22-
},
23-
icon: 'chart-line',
24-
},
25-
];
26-
27-
const expectedSectionsForItem: PartialMenuSection[] = [
28-
{
29-
visible: true,
30-
model: {
31-
type: MenuItemType.LINK,
32-
text: 'menu.section.statistics',
33-
link: `statistics/items/test-item-uuid`,
34-
},
35-
icon: 'chart-line',
36-
},
37-
];
38-
39-
const expectedSectionsForItemInvisible: PartialMenuSection[] = [
40-
{
41-
visible: false,
42-
model: {
43-
type: MenuItemType.LINK,
44-
text: 'menu.section.statistics',
45-
link: `statistics/items/test-item-uuid`,
46-
},
47-
icon: 'chart-line',
48-
},
49-
];
50-
5111
let provider: StatisticsMenuProvider;
52-
53-
const item: Item = Object.assign(new Item(), {
54-
uuid: 'test-item-uuid',
55-
type: ITEM.value,
56-
_links: { self: { href: 'self-link' } },
57-
metadata: {
58-
'dc.title': [{
59-
'value': 'Untyped Item',
60-
}],
61-
},
62-
});
63-
64-
const item2: Item = Object.assign(new Item(), {
65-
uuid: 'test-item2-uuid',
66-
type: ITEM.value,
67-
_links: { self: { href: 'self-link' } },
68-
metadata: {
69-
'dc.title': [{
70-
'value': 'Untyped Item 2',
71-
}],
72-
},
73-
});
7412
let authorizationService: AuthorizationDataService;
7513

7614
beforeEach(() => {
@@ -83,6 +21,7 @@ describe('StatisticsMenuProvider', () => {
8321
providers: [
8422
StatisticsMenuProvider,
8523
{ provide: AuthorizationDataService, useValue: authorizationService },
24+
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub({}, { }) },
8625
],
8726
});
8827
provider = TestBed.inject(StatisticsMenuProvider);
@@ -93,62 +32,21 @@ describe('StatisticsMenuProvider', () => {
9332
});
9433

9534
describe('getSectionsForContext', () => {
96-
it('should return the general statistics link when no DSO is provided', (done) => {
35+
it('should return menu entries when at least one authorization is granted', (done) => {
9736
provider.getSectionsForContext(undefined).subscribe((sections) => {
98-
expect(sections).toEqual(expectedSectionsNoDSO);
99-
done();
100-
});
101-
});
102-
it('should return a statistics link to the DSO when a DSO is provided', (done) => {
103-
provider.getSectionsForContext(item).subscribe((sections) => {
104-
expect(sections).toEqual(expectedSectionsForItem);
105-
done();
106-
});
107-
});
108-
it('should not return anything if not authorized to view statistics', (done) => {
109-
(TestBed.inject(AuthorizationDataService) as any).isAuthorized.and.returnValue(of(false));
110-
provider.getSectionsForContext(item).subscribe((sections) => {
111-
expect(sections).toEqual(expectedSectionsForItemInvisible);
112-
done();
113-
});
114-
});
115-
});
116-
117-
describe('getRouteContext', () => {
118-
it('should get the dso from the route', (done) => {
119-
const route = { data: { dso: createSuccessfulRemoteDataObject(item) } } as any;
120-
121-
provider.getRouteContext(route, undefined).subscribe((dso) => {
122-
expect(dso).toEqual(item);
37+
expect(Array.isArray(sections)).toBeTrue();
38+
expect(sections.length).toBeGreaterThan(0);
39+
expect(sections.some((s) => s.id === 'statistics')).toBeTrue();
12340
done();
12441
});
12542
});
126-
it('should get the dso from first parent route with a dso when the route itself has none', (done) => {
127-
const route = {
128-
data: {},
129-
parent: {
130-
data: {},
131-
parent: {
132-
data: { dso: createSuccessfulRemoteDataObject(item) },
133-
parent: { data: { dso: createSuccessfulRemoteDataObject(item2) } },
134-
},
135-
},
136-
} as any;
13743

138-
provider.getRouteContext(route, undefined).subscribe((dso) => {
139-
expect(dso).toEqual(item);
140-
expect(dso).not.toEqual(item2);
141-
done();
142-
});
143-
});
144-
it('should return undefined when no dso is found in the route', (done) => {
145-
const route = { data: {}, parent: { data: {}, parent: { data: {}, parent: { data: {} } } } } as any;
146-
147-
provider.getRouteContext(route, undefined).subscribe((dso) => {
148-
expect(dso).toBeUndefined();
44+
it('should return an empty array when no authorizations are granted', (done) => {
45+
(TestBed.inject(AuthorizationDataService) as any).isAuthorized.and.returnValue(of(false));
46+
provider.getSectionsForContext(undefined).subscribe((sections) => {
47+
expect(sections).toEqual([]);
14948
done();
15049
});
15150
});
15251
});
153-
15452
});

0 commit comments

Comments
 (0)