diff --git a/config/environment.default.js b/config/environment.default.js index 804d80b0f2..84fd7405e2 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -149,7 +149,32 @@ module.exports = { // Limit for years to display using jumps of five years (current year - fiveYearLimit) fiveYearLimit: 30, // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) - defaultLowerLimit: 1900 + defaultLowerLimit: 1900, + // List of all the active Browse-By types + // Adding a type will activate their Browse-By page and add them to the global navigation menu, as well as community and collection pages + // Allowed fields and their purpose: + // id: The browse id to use for fetching info from the rest api + // type: The type of Browse-By page to display + // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') + types: [ + { + id: 'title', + type: 'title' + }, + { + id: 'dateissued', + type: 'date', + metadataField: 'dc.date.issued' + }, + { + id: 'author', + type: 'metadata' + }, + { + id: 'subject', + type: 'metadata' + } + ] }, item: { edit: { diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a066ffe9d0..e53ea9f2e8 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -781,7 +781,7 @@ "control_panel": "Control Panel", "browse_global": "All of DSpace", "browse_global_communities_and_collections": "Communities & Collections", - "browse_global_by_issue_date": "By Issue Date", + "browse_global_by_dateissued": "By Issue Date", "browse_global_by_author": "By Author", "browse_global_by_title": "By Title", "browse_global_by_subject": "By Subject", diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts index 2acd96adb0..98d0cc9cd1 100644 --- a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts @@ -13,6 +13,7 @@ import { BrowseService } from '../../core/browse/browse.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; +import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-date-page', @@ -21,9 +22,10 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; }) /** * Component for browsing items by metadata definition of type 'date' - * A metadata definition is a short term used to describe one or multiple metadata fields. + * A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields. * An example would be 'dateissued' for 'dc.date.issued' */ +@rendersBrowseBy(BrowseByType.Date) export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { /** @@ -53,12 +55,12 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { }) .subscribe((params) => { const metadataField = params.metadataField || this.defaultMetadataField; - this.metadata = params.metadata || this.defaultMetadata; + this.browseId = params.id || this.defaultBrowseId; this.startsWith = +params.startsWith || params.startsWith; - const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata); + const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.browseId); this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); - this.updateStartsWithOptions(this.metadata, metadataField, params.scope); + this.updateStartsWithOptions(this.browseId, metadataField, params.scope); })); } @@ -78,8 +80,9 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { let lowerLimit = this.config.browseBy.defaultLowerLimit; if (hasValue(firstItemRD.payload)) { const date = firstItemRD.payload.firstMetadataValue(metadataField); - if (hasValue(date) && hasValue(+date.split('-')[0])) { - lowerLimit = +date.split('-')[0]; + if (hasValue(date)) { + const dateObj = new Date(date); + lowerLimit = dateObj.getFullYear(); } } const options = []; diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index cf43f74eb0..c589c543d4 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -1,7 +1,7 @@
{ - this.metadata = params.metadata || this.defaultMetadata; + this.browseId = params.id || this.defaultBrowseId; this.value = +params.value || params.value || ''; this.startsWith = +params.startsWith || params.startsWith; - const searchOptions = browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata); + const searchOptions = browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.browseId); if (isNotEmpty(this.value)) { this.updatePageWithItems(searchOptions, this.value); } else { diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts new file mode 100644 index 0000000000..f54efb9378 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts @@ -0,0 +1,12 @@ +import { BrowseByType, rendersBrowseBy } from './browse-by-decorator'; + +describe('BrowseByDecorator', () => { + const titleDecorator = rendersBrowseBy(BrowseByType.Title); + const dateDecorator = rendersBrowseBy(BrowseByType.Date); + const metadataDecorator = rendersBrowseBy(BrowseByType.Metadata); + it('should have a decorator for all types', () => { + expect(titleDecorator.length).not.toEqual(0); + expect(dateDecorator.length).not.toEqual(0); + expect(metadataDecorator.length).not.toEqual(0); + }); +}); diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts new file mode 100644 index 0000000000..0143377922 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts @@ -0,0 +1,37 @@ +import { hasNoValue } from '../../shared/empty.util'; + +export enum BrowseByType { + Title = 'title', + Metadata = 'metadata', + Date = 'date' +} + +export const DEFAULT_BROWSE_BY_TYPE = BrowseByType.Metadata; + +const map = new Map(); + +/** + * Decorator used for rendering Browse-By pages by type + * @param browseByType The type of page + */ +export function rendersBrowseBy(browseByType: BrowseByType) { + return function decorator(component: any) { + if (hasNoValue(map.get(browseByType))) { + map.set(browseByType, component); + } else { + throw new Error(`There can't be more than one component to render Browse-By of type "${browseByType}"`); + } + }; +} + +/** + * Get the component used for rendering a Browse-By page by type + * @param browseByType The type of page + */ +export function getComponentByBrowseByType(browseByType) { + const comp = map.get(browseByType); + if (hasNoValue(comp)) { + map.get(DEFAULT_BROWSE_BY_TYPE); + } + return comp; +} diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html new file mode 100644 index 0000000000..afe79cf2b1 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html @@ -0,0 +1 @@ + diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts new file mode 100644 index 0000000000..85805debe0 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts @@ -0,0 +1,55 @@ +import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config'; +import { ActivatedRoute } from '@angular/router'; +import * as decorator from './browse-by-decorator'; +import createSpy = jasmine.createSpy; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; + +describe('BrowseBySwitcherComponent', () => { + let comp: BrowseBySwitcherComponent; + let fixture: ComponentFixture; + + const types = ENV_CONFIG.browseBy.types; + + const params = new BehaviorSubject(createParamsWithId('initialValue')); + + const activatedRouteStub = { + params: params + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BrowseBySwitcherComponent ], + providers: [ + { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, + { provide: ActivatedRoute, useValue: activatedRouteStub } + ], + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(BrowseBySwitcherComponent); + comp = fixture.componentInstance; + spyOnProperty(decorator, 'getComponentByBrowseByType').and.returnValue(createSpy('getComponentByItemType')); + })); + + types.forEach((type) => { + describe(`when switching to a browse-by page for "${type.id}"`, () => { + beforeEach(() => { + params.next(createParamsWithId(type.id)); + fixture.detectChanges(); + }); + + it(`should call getComponentByBrowseByType with type "${type.type}"`, () => { + expect(decorator.getComponentByBrowseByType).toHaveBeenCalledWith(type.type); + }); + }); + }); +}); + +export function createParamsWithId(id) { + return { id: id }; +} diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts new file mode 100644 index 0000000000..1ae8927f0a --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts @@ -0,0 +1,40 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/internal/Observable'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; +import { map, tap } from 'rxjs/operators'; +import { getComponentByBrowseByType } from './browse-by-decorator'; + +@Component({ + selector: 'ds-browse-by-switcher', + templateUrl: './browse-by-switcher.component.html' +}) +/** + * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided + */ +export class BrowseBySwitcherComponent implements OnInit { + + /** + * Resolved browse-by component + */ + browseByComponent: Observable; + + public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected route: ActivatedRoute) { + } + + /** + * Fetch the correct browse-by component by using the relevant config from environment.js + */ + ngOnInit(): void { + this.browseByComponent = this.route.params.pipe( + map((params) => { + const id = params.id; + return this.config.browseBy.types.find((config: BrowseByTypeConfig) => config.id === id); + }), + map((config: BrowseByTypeConfig) => getComponentByBrowseByType(config.type)) + ); + } + +} diff --git a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.ts b/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.ts index 717275bf8b..91713cd219 100644 --- a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.ts @@ -1,6 +1,5 @@ import { combineLatest as observableCombineLatest } from 'rxjs'; import { Component } from '@angular/core'; -import { ItemDataService } from '../../core/data/item-data.service'; import { ActivatedRoute, Router } from '@angular/router'; import { hasValue } from '../../shared/empty.util'; import { @@ -11,6 +10,7 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search- import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { BrowseService } from '../../core/browse/browse.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; +import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-title-page', @@ -20,6 +20,7 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options /** * Component for browsing items by title (dc.title) */ +@rendersBrowseBy(BrowseByType.Title) export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { public constructor(protected route: ActivatedRoute, @@ -41,8 +42,8 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { return Object.assign({}, params, queryParams, data); }) .subscribe((params) => { - this.metadata = params.metadata || this.defaultMetadata; - this.updatePageWithItems(browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata), undefined); + this.browseId = params.id || this.defaultBrowseId; + this.updatePageWithItems(browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.browseId), undefined); this.updateParent(params.scope) })); this.updateStartsWithTextOptions(); diff --git a/src/app/+browse-by/browse-by-guard.spec.ts b/src/app/+browse-by/browse-by-guard.spec.ts index 784f9ea22d..da45437a78 100644 --- a/src/app/+browse-by/browse-by-guard.spec.ts +++ b/src/app/+browse-by/browse-by-guard.spec.ts @@ -1,6 +1,7 @@ import { first } from 'rxjs/operators'; import { BrowseByGuard } from './browse-by-guard'; import { of as observableOf } from 'rxjs'; +import { ENV_CONFIG } from '../../config'; describe('BrowseByGuard', () => { describe('canActivate', () => { @@ -11,20 +12,20 @@ describe('BrowseByGuard', () => { const name = 'An interesting DSO'; const title = 'Author'; const field = 'Author'; - const metadata = 'author'; + const id = 'author'; const metadataField = 'dc.contributor'; const scope = '1234-65487-12354-1235'; const value = 'Filter'; beforeEach(() => { dsoService = { - findById: (id: string) => observableOf({ payload: { name: name }, hasSucceeded: true }) + findById: (dsoId: string) => observableOf({ payload: { name: name }, hasSucceeded: true }) }; translateService = { instant: () => field }; - guard = new BrowseByGuard(dsoService, translateService); + guard = new BrowseByGuard(ENV_CONFIG, dsoService, translateService); }); it('should return true, and sets up the data correctly, with a scope and value', () => { @@ -34,7 +35,7 @@ describe('BrowseByGuard', () => { metadataField, }, params: { - metadata, + id, }, queryParams: { scope, @@ -47,7 +48,7 @@ describe('BrowseByGuard', () => { (canActivate) => { const result = { title, - metadata, + id, metadataField, collection: name, field, @@ -66,7 +67,7 @@ describe('BrowseByGuard', () => { metadataField, }, params: { - metadata, + id, }, queryParams: { scope @@ -79,7 +80,7 @@ describe('BrowseByGuard', () => { (canActivate) => { const result = { title, - metadata, + id, metadataField, collection: name, field, @@ -98,7 +99,7 @@ describe('BrowseByGuard', () => { metadataField, }, params: { - metadata, + id, }, queryParams: { value @@ -110,7 +111,7 @@ describe('BrowseByGuard', () => { (canActivate) => { const result = { title, - metadata, + id, metadataField, collection: '', field, diff --git a/src/app/+browse-by/browse-by-guard.ts b/src/app/+browse-by/browse-by-guard.ts index 5d3dad2b0f..3813f7e656 100644 --- a/src/app/+browse-by/browse-by-guard.ts +++ b/src/app/+browse-by/browse-by-guard.ts @@ -1,11 +1,12 @@ import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; -import { Injectable } from '@angular/core'; +import { Inject, Injectable } from '@angular/core'; import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service'; -import { hasValue } from '../shared/empty.util'; +import { hasNoValue, hasValue } from '../shared/empty.util'; import { map } from 'rxjs/operators'; import { getSucceededRemoteData } from '../core/shared/operators'; import { TranslateService } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../config'; @Injectable() /** @@ -13,36 +14,43 @@ import { of as observableOf } from 'rxjs'; */ export class BrowseByGuard implements CanActivate { - constructor(protected dsoService: DSpaceObjectDataService, + constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected dsoService: DSpaceObjectDataService, protected translate: TranslateService) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const title = route.data.title; - const metadata = route.params.metadata || route.queryParams.metadata || route.data.metadata; - const metadataField = route.data.metadataField; + const id = route.params.id || route.queryParams.id || route.data.id; + let metadataField = route.data.metadataField; + if (hasNoValue(metadataField) && hasValue(id)) { + const config = this.config.browseBy.types.find((conf) => conf.id === id); + if (hasValue(config) && hasValue(config.metadataField)) { + metadataField = config.metadataField; + } + } const scope = route.queryParams.scope; const value = route.queryParams.value; - const metadataTranslated = this.translate.instant('browse.metadata.' + metadata); + const metadataTranslated = this.translate.instant('browse.metadata.' + id); if (hasValue(scope)) { const dsoAndMetadata$ = this.dsoService.findById(scope).pipe(getSucceededRemoteData()); return dsoAndMetadata$.pipe( map((dsoRD) => { const name = dsoRD.payload.name; - route.data = this.createData(title, metadata, metadataField, name, metadataTranslated, value); + route.data = this.createData(title, id, metadataField, name, metadataTranslated, value); return true; }) ); } else { - route.data = this.createData(title, metadata, metadataField, '', metadataTranslated, value); + route.data = this.createData(title, id, metadataField, '', metadataTranslated, value); return observableOf(true); } } - private createData(title, metadata, metadataField, collection, field, value) { + private createData(title, id, metadataField, collection, field, value) { return { title: title, - metadata: metadata, + id: id, metadataField: metadataField, collection: collection, field: field, diff --git a/src/app/+browse-by/browse-by-routing.module.ts b/src/app/+browse-by/browse-by-routing.module.ts index 9ba15ecfe9..e549c0f4e6 100644 --- a/src/app/+browse-by/browse-by-routing.module.ts +++ b/src/app/+browse-by/browse-by-routing.module.ts @@ -1,16 +1,12 @@ import { RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import { BrowseByTitlePageComponent } from './+browse-by-title-page/browse-by-title-page.component'; -import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse-by-metadata-page.component'; -import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date-page.component'; import { BrowseByGuard } from './browse-by-guard'; +import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component'; @NgModule({ imports: [ RouterModule.forChild([ - { path: 'title', component: BrowseByTitlePageComponent, canActivate: [BrowseByGuard], data: { metadata: 'title', title: 'browse.title' } }, - { path: 'dateissued', component: BrowseByDatePageComponent, canActivate: [BrowseByGuard], data: { metadata: 'dateissued', metadataField: 'dc.date.issued', title: 'browse.title' } }, - { path: ':metadata', component: BrowseByMetadataPageComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } } + { path: ':id', component: BrowseBySwitcherComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } } ]) ] }) diff --git a/src/app/+browse-by/browse-by.module.ts b/src/app/+browse-by/browse-by.module.ts index 30d4617c16..4938b0df15 100644 --- a/src/app/+browse-by/browse-by.module.ts +++ b/src/app/+browse-by/browse-by.module.ts @@ -8,6 +8,7 @@ import { BrowseService } from '../core/browse/browse.service'; import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse-by-metadata-page.component'; import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date-page.component'; import { BrowseByGuard } from './browse-by-guard'; +import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component'; @NgModule({ imports: [ @@ -18,12 +19,18 @@ import { BrowseByGuard } from './browse-by-guard'; declarations: [ BrowseByTitlePageComponent, BrowseByMetadataPageComponent, - BrowseByDatePageComponent + BrowseByDatePageComponent, + BrowseBySwitcherComponent ], providers: [ ItemDataService, BrowseService, BrowseByGuard + ], + entryComponents: [ + BrowseByTitlePageComponent, + BrowseByMetadataPageComponent, + BrowseByDatePageComponent ] }) export class BrowseByModule { diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts index 2d937fd84e..ca054a662b 100644 --- a/src/app/navbar/navbar.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -11,6 +11,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { MenuService } from '../shared/menu/menu.service'; import { MenuServiceStub } from '../shared/testing/menu-service-stub'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../../config'; let comp: NavbarComponent; let fixture: ComponentFixture; @@ -30,6 +31,7 @@ describe('NavbarComponent', () => { { provide: Injector, useValue: {} }, { provide: MenuService, useValue: menuService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, + { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG } ], schemas: [NO_ERRORS_SCHEMA] }) diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index 48b316af4b..1a20a6b12f 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnInit } from '@angular/core'; +import { Component, Inject, Injector, OnInit } from '@angular/core'; import { slideMobileNav } from '../shared/animations/slide'; import { MenuComponent } from '../shared/menu/menu.component'; import { MenuService } from '../shared/menu/menu.service'; @@ -6,6 +6,7 @@ import { MenuID, MenuItemType } from '../shared/menu/initial-menus-state'; import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../config'; /** * Component representing the public navbar @@ -23,7 +24,8 @@ export class NavbarComponent extends MenuComponent implements OnInit { */ menuID = MenuID.PUBLIC; - constructor(protected menuService: MenuService, + constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected menuService: MenuService, protected injector: Injector, public windowService: HostWindowService ) { @@ -39,7 +41,7 @@ export class NavbarComponent extends MenuComponent implements OnInit { * Initialize all menu sections and items for this menu */ createMenu() { - const menuList = [ + const menuList: any[] = [ /* News */ { id: 'browse_global', @@ -62,50 +64,6 @@ export class NavbarComponent extends MenuComponent implements OnInit { // link: '#' // } as LinkMenuItemModel, // }, - { - id: 'browse_global_global_by_title', - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_title', - link: '/browse/title' - } as LinkMenuItemModel, - }, - { - id: 'browse_global_global_by_issue_date', - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_issue_date', - link: '/browse/dateissued' - } as LinkMenuItemModel, - }, - { - id: 'browse_global_by_author', - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_author', - link: '/browse/author' - } as LinkMenuItemModel, - }, - { - id: 'browse_global_by_subject', - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_subject', - link: '/browse/subject' - } as LinkMenuItemModel, - }, /* Statistics */ { @@ -120,6 +78,21 @@ export class NavbarComponent extends MenuComponent implements OnInit { index: 2 }, ]; + // Read the different Browse-By types from config and add them to the browse menu + const types = this.config.browseBy.types; + types.forEach((typeConfig) => { + menuList.push({ + id: `browse_global_by_${typeConfig.id}`, + parentID: 'browse_global', + active: false, + visible: true, + model: { + type: MenuItemType.LINK, + text: `menu.section.browse_global_by_${typeConfig.id}`, + link: `/browse/${typeConfig.id}` + } as LinkMenuItemModel + }); + }); menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection)); } diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html index f9ef4e5232..1c73fbb3df 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html @@ -1,7 +1,6 @@

{{'browse.comcol.head' | translate}}

diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts index 85d40a77e0..dcc7840bb4 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -1,4 +1,6 @@ -import { Component, Input } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; /** * A component to display the "Browse By" section of a Community or Collection page @@ -8,9 +10,22 @@ import { Component, Input } from '@angular/core'; selector: 'ds-comcol-page-browse-by', templateUrl: './comcol-page-browse-by.component.html', }) -export class ComcolPageBrowseByComponent { +export class ComcolPageBrowseByComponent implements OnInit { /** * The ID of the Community or Collection */ @Input() id: string; + + /** + * List of currently active browse configurations + */ + types: BrowseByTypeConfig[]; + + constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig) { + } + + ngOnInit(): void { + this.types = this.config.browseBy.types; + } + } diff --git a/src/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts index 6adba66b92..719e127b4b 100644 --- a/src/config/browse-by-config.interface.ts +++ b/src/config/browse-by-config.interface.ts @@ -1,4 +1,5 @@ import { Config } from './config.interface'; +import { BrowseByTypeConfig } from './browse-by-type-config.interface'; /** * Config that determines how the dropdown list of years are created for browse-by-date components @@ -18,4 +19,9 @@ export interface BrowseByConfig extends Config { * The absolute lowest year to display in the dropdown when no lowest date can be found for all items */ defaultLowerLimit: number; + + /** + * A list of all the active Browse-By pages + */ + types: BrowseByTypeConfig[]; } diff --git a/src/config/browse-by-type-config.interface.ts b/src/config/browse-by-type-config.interface.ts new file mode 100644 index 0000000000..97d3acbe25 --- /dev/null +++ b/src/config/browse-by-type-config.interface.ts @@ -0,0 +1,23 @@ +import { Config } from './config.interface'; +import { BrowseByType } from '../app/+browse-by/+browse-by-switcher/browse-by-decorator'; + +/** + * Config used for rendering Browse-By pages and links + */ +export interface BrowseByTypeConfig extends Config { + /** + * The browse id used for fetching browse data from the rest api + * e.g. author + */ + id: string; + + /** + * The type of Browse-By page to render + */ + type: BrowseByType; + + /** + * The metadata field to use for rendering starts-with options (only necessary when type is set to BrowseByType.Date) + */ + metadataField: string; +}