Fix merge issues and tests

- Migrate create-report sections to a new provider
- Fix menu component test
- Add dso option sections to com/col sub paths
- Fix issue with breadcrumbs on the collection page
This commit is contained in:
Yana De Pauw
2025-02-14 13:11:15 +01:00
parent c19af67009
commit 0fec5395ff
11 changed files with 240 additions and 17 deletions

View File

@@ -106,7 +106,7 @@ export const APP_ROUTES: Route[] = [
path: COLLECTION_MODULE_PATH, path: COLLECTION_MODULE_PATH,
loadChildren: () => import('./collection-page/collection-page-routes') loadChildren: () => import('./collection-page/collection-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
data: { showBreadcrumbs: false, enableRSS: true }, data: { enableRSS: true },
canActivate: [endUserAgreementCurrentUserGuard], canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {

View File

@@ -13,6 +13,7 @@ import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.me
import { BrowseMenuProvider } from './shared/menu/providers/browse.menu'; import { BrowseMenuProvider } from './shared/menu/providers/browse.menu';
import { SubscribeMenuProvider } from './shared/menu/providers/comcol-subscribe.menu'; import { SubscribeMenuProvider } from './shared/menu/providers/comcol-subscribe.menu';
import { CommunityListMenuProvider } from './shared/menu/providers/community-list.menu'; import { CommunityListMenuProvider } from './shared/menu/providers/community-list.menu';
import { CreateReportMenuProvider } from './shared/menu/providers/create-report.menu';
import { CurationMenuProvider } from './shared/menu/providers/curation.menu'; import { CurationMenuProvider } from './shared/menu/providers/curation.menu';
import { DSpaceObjectEditMenuProvider } from './shared/menu/providers/dso-edit.menu'; import { DSpaceObjectEditMenuProvider } from './shared/menu/providers/dso-edit.menu';
import { DsoOptionMenuProvider } from './shared/menu/providers/dso-option.menu'; import { DsoOptionMenuProvider } from './shared/menu/providers/dso-option.menu';
@@ -42,6 +43,7 @@ export const MENUS = buildMenuStructure({
ImportMenuProvider, ImportMenuProvider,
ExportMenuProvider, ExportMenuProvider,
AccessControlMenuProvider, AccessControlMenuProvider,
CreateReportMenuProvider,
AdminSearchMenuProvider, AdminSearchMenuProvider,
RegistriesMenuProvider, RegistriesMenuProvider,
CurationMenuProvider, CurationMenuProvider,
@@ -54,11 +56,17 @@ export const MENUS = buildMenuStructure({
DsoOptionMenuProvider.withSubs([ DsoOptionMenuProvider.withSubs([
SubscribeMenuProvider.onRoute( SubscribeMenuProvider.onRoute(
MenuRoute.SIMPLE_COMMUNITY_PAGE, MenuRoute.SIMPLE_COMMUNITY_PAGE,
MenuRoute.SIMPLE_COMMUNITY_BROWSE_PAGE,
MenuRoute.SIMPLE_COMMUNITY_SUBCOLCOM_PAGE,
MenuRoute.SIMPLE_COLLECTION_PAGE, MenuRoute.SIMPLE_COLLECTION_PAGE,
MenuRoute.SIMPLE_COLLECTION_BROWSE_PAGE,
), ),
DSpaceObjectEditMenuProvider.onRoute( DSpaceObjectEditMenuProvider.onRoute(
MenuRoute.SIMPLE_COMMUNITY_PAGE, MenuRoute.SIMPLE_COMMUNITY_PAGE,
MenuRoute.SIMPLE_COMMUNITY_BROWSE_PAGE,
MenuRoute.SIMPLE_COMMUNITY_SUBCOLCOM_PAGE,
MenuRoute.SIMPLE_COLLECTION_PAGE, MenuRoute.SIMPLE_COLLECTION_PAGE,
MenuRoute.SIMPLE_COLLECTION_BROWSE_PAGE,
MenuRoute.SIMPLE_ITEM_PAGE, MenuRoute.SIMPLE_ITEM_PAGE,
MenuRoute.FULL_ITEM_PAGE, MenuRoute.FULL_ITEM_PAGE,
), ),

View File

@@ -97,7 +97,10 @@ export const ROUTES: Route[] = [
resolve: { resolve: {
breadcrumb: browseByI18nBreadcrumbResolver, breadcrumb: browseByI18nBreadcrumbResolver,
}, },
data: { breadcrumbKey: 'browse.metadata' }, data: {
breadcrumbKey: 'browse.metadata',
menuRoute: MenuRoute.SIMPLE_COLLECTION_BROWSE_PAGE,
},
}, },
], ],
}, },

View File

@@ -83,7 +83,10 @@ export const ROUTES: Route[] = [
resolve: { resolve: {
breadcrumb: i18nBreadcrumbResolver, breadcrumb: i18nBreadcrumbResolver,
}, },
data: { breadcrumbKey: 'community.subcoms-cols' }, data: {
breadcrumbKey: 'community.subcoms-cols',
menuRoute: MenuRoute.SIMPLE_COMMUNITY_SUBCOLCOM_PAGE,
},
}, },
{ {
path: 'browse/:id', path: 'browse/:id',
@@ -93,7 +96,10 @@ export const ROUTES: Route[] = [
resolve: { resolve: {
breadcrumb: browseByI18nBreadcrumbResolver, breadcrumb: browseByI18nBreadcrumbResolver,
}, },
data: { breadcrumbKey: 'browse.metadata' }, data: {
breadcrumbKey: 'browse.metadata',
menuRoute: MenuRoute.SIMPLE_COMMUNITY_BROWSE_PAGE,
},
}, },
], ],
}, },

View File

@@ -3,7 +3,10 @@
*/ */
export enum MenuRoute { export enum MenuRoute {
SIMPLE_COMMUNITY_PAGE = 'simple-community-page', SIMPLE_COMMUNITY_PAGE = 'simple-community-page',
SIMPLE_COMMUNITY_BROWSE_PAGE = 'simple-community-browse-page',
SIMPLE_COMMUNITY_SUBCOLCOM_PAGE = 'simple-community-subcolcom-page',
SIMPLE_COLLECTION_PAGE = 'simple-collection-page', SIMPLE_COLLECTION_PAGE = 'simple-collection-page',
SIMPLE_COLLECTION_BROWSE_PAGE = 'simple-collection-browse-page',
SIMPLE_ITEM_PAGE = 'simple-item-page', SIMPLE_ITEM_PAGE = 'simple-item-page',
FULL_ITEM_PAGE = 'full-item-page', FULL_ITEM_PAGE = 'full-item-page',
} }

View File

@@ -125,6 +125,7 @@ describe('MenuComponent', () => {
id: 'section1', id: 'section1',
active: false, active: false,
visible: true, visible: true,
alwaysRenderExpandable: false,
model: { model: {
type: MenuItemType.LINK, type: MenuItemType.LINK,
text: 'test', text: 'test',
@@ -190,6 +191,7 @@ describe('MenuComponent', () => {
id: 'section1', id: 'section1',
active: false, active: false,
visible: true, visible: true,
alwaysRenderExpandable: false,
model: { model: {
type: MenuItemType.LINK, type: MenuItemType.LINK,
text: 'test', text: 'test',
@@ -201,6 +203,7 @@ describe('MenuComponent', () => {
parentID: 'section1', parentID: 'section1',
active: false, active: false,
visible: true, visible: true,
alwaysRenderExpandable: false,
model: { model: {
type: MenuItemType.LINK, type: MenuItemType.LINK,
text: 'test', text: 'test',

View File

@@ -0,0 +1,100 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { TestBed } from '@angular/core/testing';
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 { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { ConfigurationProperty } from '../../../core/shared/configuration-property.model';
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
import { AuthorizationDataServiceStub } from '../../testing/authorization-service.stub';
import { ConfigurationDataServiceStub } from '../../testing/configuration-data.service.stub';
import { LinkMenuItemModel } from '../menu-item/models/link.model';
import { TextMenuItemModel } from '../menu-item/models/text.model';
import { MenuItemType } from '../menu-item-type.model';
import { PartialMenuSection } from '../menu-provider.model';
import { CreateReportMenuProvider } from './create-report.menu';
describe('CreateReportMenuProvider', () => {
const expectedTopSection: PartialMenuSection = {
visible: true,
model: {
type: MenuItemType.TEXT,
text: 'menu.section.reports',
} as TextMenuItemModel,
icon: 'file-alt',
};
const expectedSubSections: PartialMenuSection[] = [
{
visible: true,
model: {
type: MenuItemType.LINK,
text: 'menu.section.reports.collections',
link: '/admin/reports/collections',
} as LinkMenuItemModel,
icon: 'user-check',
},
{
visible: true,
model: {
type: MenuItemType.LINK,
text: 'menu.section.reports.queries',
link: '/admin/reports/queries',
} as LinkMenuItemModel,
icon: 'user-check',
},
];
let provider: CreateReportMenuProvider;
let authorizationServiceStub = new AuthorizationDataServiceStub();
let configurationDataService = new ConfigurationDataServiceStub();
beforeEach(() => {
spyOn(authorizationServiceStub, 'isAuthorized').and.callFake((id: FeatureID) => {
if (id === FeatureID.CanManageGroups) {
return observableOf(false);
} else {
return observableOf(true);
}
});
spyOn(configurationDataService, 'findByPropertyName').and.callFake((property: string) => {
return createSuccessfulRemoteDataObject$(Object.assign({}, new ConfigurationProperty(), { values: ['true'] }));
});
TestBed.configureTestingModule({
providers: [
CreateReportMenuProvider,
{ provide: AuthorizationDataService, useValue: authorizationServiceStub },
{ provide: ConfigurationDataService, useValue: configurationDataService },
],
});
provider = TestBed.inject(CreateReportMenuProvider);
});
it('should be created', () => {
expect(provider).toBeTruthy();
});
it('getTopSection should return expected menu section', (done) => {
provider.getTopSection().subscribe((section) => {
expect(section).toEqual(expectedTopSection);
done();
});
});
it('getSubSections should return expected menu sections', (done) => {
provider.getSubSections().subscribe((sections) => {
expect(sections).toEqual(expectedSubSections);
done();
});
});
});

View File

@@ -0,0 +1,93 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { Injectable } from '@angular/core';
import {
combineLatest as observableCombineLatest,
Observable,
} from 'rxjs';
import { map } 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 { RemoteData } from '../../../core/data/remote-data';
import { ConfigurationProperty } from '../../../core/shared/configuration-property.model';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { LinkMenuItemModel } from '../menu-item/models/link.model';
import { TextMenuItemModel } from '../menu-item/models/text.model';
import { MenuItemType } from '../menu-item-type.model';
import { PartialMenuSection } from '../menu-provider.model';
import { AbstractExpandableMenuProvider } from './helper-providers/expandable-menu-provider';
/**
* Menu provider to create the report menu sections
*/
@Injectable()
export class CreateReportMenuProvider extends AbstractExpandableMenuProvider {
constructor(
protected authorizationService: AuthorizationDataService,
protected configurationDataService: ConfigurationDataService,
) {
super();
}
getSubSections(): Observable<PartialMenuSection[]> {
return observableCombineLatest([
this.configurationDataService.findByPropertyName('contentreport.enable').pipe(
getFirstCompletedRemoteData(),
map((res: RemoteData<ConfigurationProperty>) => res.hasSucceeded && res.payload && res.payload.values[0] === 'true'),
),
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
]).pipe(
map(([reportEnabled, isSiteAdmin]: [boolean, boolean]) => {
return [
/* Collections Report */
{
visible: isSiteAdmin && reportEnabled,
model: {
type: MenuItemType.LINK,
text: 'menu.section.reports.collections',
link: '/admin/reports/collections',
} as LinkMenuItemModel,
icon: 'user-check',
},
/* Queries Report */
{
visible: isSiteAdmin && reportEnabled,
model: {
type: MenuItemType.LINK,
text: 'menu.section.reports.queries',
link: '/admin/reports/queries',
} as LinkMenuItemModel,
icon: 'user-check',
},
];
}));
}
getTopSection(): Observable<PartialMenuSection> {
return observableCombineLatest([
this.configurationDataService.findByPropertyName('contentreport.enable').pipe(
getFirstCompletedRemoteData(),
map((res: RemoteData<ConfigurationProperty>) => res.hasSucceeded && res.payload && res.payload.values[0] === 'true'),
),
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
]).pipe(
map(([reportEnabled, isSiteAdmin]: [boolean, boolean]) => {
return {
visible: isSiteAdmin && reportEnabled,
model: {
type: MenuItemType.TEXT,
text: 'menu.section.reports',
} as TextMenuItemModel,
icon: 'file-alt',
};
}));
}
}

View File

@@ -14,7 +14,7 @@ describe('DSpaceObjectPageMenuProvider', () => {
const item: Item = Object.assign(new Item(), { const item: Item = Object.assign(new Item(), {
uuid: 'test-item-uuid', uuid: 'test-item-uuid',
type: ITEM.value, type: ITEM.value,
_links: {self: {href: 'self-link'}}, _links: { self: { href: 'self-link' } },
metadata: { metadata: {
'dc.title': [{ 'dc.title': [{
'value': 'Untyped Item', 'value': 'Untyped Item',
@@ -25,7 +25,7 @@ describe('DSpaceObjectPageMenuProvider', () => {
const item2: Item = Object.assign(new Item(), { const item2: Item = Object.assign(new Item(), {
uuid: 'test-item2-uuid', uuid: 'test-item2-uuid',
type: ITEM.value, type: ITEM.value,
_links: {self: {href: 'self-link'}}, _links: { self: { href: 'self-link' } },
metadata: { metadata: {
'dc.title': [{ 'dc.title': [{
'value': 'Untyped Item 2', 'value': 'Untyped Item 2',
@@ -36,7 +36,7 @@ describe('DSpaceObjectPageMenuProvider', () => {
const person: Item = Object.assign(new Item(), { const person: Item = Object.assign(new Item(), {
uuid: 'test-uuid', uuid: 'test-uuid',
type: ITEM.value, type: ITEM.value,
_links: {self: {href: 'self-link'}}, _links: { self: { href: 'self-link' } },
metadata: { metadata: {
'dc.title': [{ 'dc.title': [{
'value': 'Person Entity', 'value': 'Person Entity',
@@ -50,7 +50,7 @@ describe('DSpaceObjectPageMenuProvider', () => {
const collection: Collection = Object.assign(new Collection(), { const collection: Collection = Object.assign(new Collection(), {
uuid: 'test-collection-uuid', uuid: 'test-collection-uuid',
type: COLLECTION.value, type: COLLECTION.value,
_links: {self: {href: 'self-link'}}, _links: { self: { href: 'self-link' } },
metadata: { metadata: {
'dc.title': [{ 'dc.title': [{
'value': 'Collection', 'value': 'Collection',
@@ -75,7 +75,7 @@ describe('DSpaceObjectPageMenuProvider', () => {
describe('getRouteContext', () => { describe('getRouteContext', () => {
it('should get the dso from the route', (done) => { it('should get the dso from the route', (done) => {
const route = {data: {dso: createSuccessfulRemoteDataObject(item)}} as any; const route = { data: { dso: createSuccessfulRemoteDataObject(item) } } as any;
provider.getRouteContext(route, undefined).subscribe((dso) => { provider.getRouteContext(route, undefined).subscribe((dso) => {
expect(dso).toEqual(item); expect(dso).toEqual(item);
@@ -89,8 +89,8 @@ describe('DSpaceObjectPageMenuProvider', () => {
parent: { parent: {
data: {}, data: {},
parent: { parent: {
data: {dso: createSuccessfulRemoteDataObject(item)}, data: { dso: createSuccessfulRemoteDataObject(item) },
parent: {data: {dso: createSuccessfulRemoteDataObject(item2)}}, parent: { data: { dso: createSuccessfulRemoteDataObject(item2) } },
}, },
}, },
} as any; } as any;
@@ -101,7 +101,7 @@ describe('DSpaceObjectPageMenuProvider', () => {
}); });
}); });
it('should return undefined when no dso is found in the route', (done) => { it('should return undefined when no dso is found in the route', (done) => {
const route = {data: {}, parent: {data: {}, parent: {data: {}, parent: {data: {}}}}} as any; const route = { data: {}, parent: { data: {}, parent: { data: {}, parent: { data: {} } } } } as any;
provider.getRouteContext(route, undefined).subscribe((dso) => { provider.getRouteContext(route, undefined).subscribe((dso) => {
expect(dso).toBeUndefined(); expect(dso).toBeUndefined();

View File

@@ -16,7 +16,10 @@ import {
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { hasNoValue, hasValue } from '../../../empty.util'; import {
hasNoValue,
hasValue,
} from '../../../empty.util';
import { AbstractRouteContextMenuProvider } from './route-context.menu'; import { AbstractRouteContextMenuProvider } from './route-context.menu';
/** /**

View File

@@ -7,15 +7,19 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { combineLatest, map, Observable, } from 'rxjs'; import {
combineLatest,
map,
Observable,
} from 'rxjs';
import { getDSORoute } from '../../../app-routing-paths';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { hasValue, } from '../../empty.util'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { hasValue } from '../../empty.util';
import { MenuItemType } from '../menu-item-type.model'; import { MenuItemType } from '../menu-item-type.model';
import { PartialMenuSection } from '../menu-provider.model'; import { PartialMenuSection } from '../menu-provider.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { getDSORoute } from '../../../app-routing-paths';
import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu';
/** /**