diff --git a/config/environment.default.js b/config/environment.default.js index f70f132fa4..3013705a2e 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -149,7 +149,26 @@ 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, + types: [ + { + metadata: 'title', + type: 'title' + }, + { + metadata: 'dateissued', + type: 'date', + metadataField: 'dc.date.issued' + }, + { + metadata: 'author', + type: 'metadata' + }, + { + metadata: 'subject', + type: 'metadata' + } + ] }, item: { edit: { 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..ab22216afa 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', @@ -24,6 +25,7 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; * A metadata definition 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 { /** @@ -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.ts b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.ts index 52c63341bd..7a85cc258f 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -15,6 +15,7 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { take } from 'rxjs/operators'; import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; +import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -26,6 +27,7 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; * A metadata definition is a short term used to describe one or multiple metadata fields. * An example would be 'author' for 'dc.contributor.*' */ +@rendersBrowseBy(BrowseByType.Metadata) export class BrowseByMetadataPageComponent implements OnInit { /** 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..fe61a2edd2 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts @@ -0,0 +1,29 @@ +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(); + +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}"`); + } + }; +} + +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..c7eab882d4 --- /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.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts new file mode 100644 index 0000000000..c3af4ddf52 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts @@ -0,0 +1,36 @@ +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' +}) +export class BrowseBySwitcherComponent implements OnInit { + + browseByTypeConfig: Observable; + + public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected route: ActivatedRoute) { + } + + ngOnInit(): void { + this.browseByTypeConfig = this.route.params.pipe( + map((params) => { + const metadata = params.metadata; + return this.config.browseBy.types.find((config: BrowseByTypeConfig) => config.metadata === metadata); + }) + ); + } + + getComponent() { + return this.browseByTypeConfig.pipe( + 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..92f9569d38 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, diff --git a/src/app/+browse-by/browse-by-guard.ts b/src/app/+browse-by/browse-by-guard.ts index 5d3dad2b0f..520d23c772 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,14 +14,21 @@ 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; + let metadataField = route.data.metadataField; + if (hasNoValue(metadataField) && hasValue(metadata)) { + const config = this.config.browseBy.types.find((conf) => conf.metadata === metadata); + 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); diff --git a/src/app/+browse-by/browse-by-routing.module.ts b/src/app/+browse-by/browse-by-routing.module.ts index 9ba15ecfe9..0b20cd4466 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: ':metadata', 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/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts index 6adba66b92..dec2f1bcf5 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,6 @@ 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; + + 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..d9ecc14704 --- /dev/null +++ b/src/config/browse-by-type-config.interface.ts @@ -0,0 +1,8 @@ +import { Config } from './config.interface'; +import { BrowseByType } from '../app/+browse-by/+browse-by-switcher/browse-by-decorator'; + +export interface BrowseByTypeConfig extends Config { + metadata: string; + type: BrowseByType; + metadataField: string; +}