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 3783b09309..2277f3d88a 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -418,7 +418,10 @@ "titleprefix": "Publication: ", "journal-title": "Journal Title", "journal-issn": "Journal ISSN", - "volume-title": "Volume Title" + "volume-title": "Volume Title", + "publisher": "Publisher", + "description": "Description" + }, "listelement": { "badge": "Publication" @@ -641,6 +644,34 @@ "birthDate": { "placeholder": "Birth Date", "head": "Birth Date" + }, + "creativeWorkPublisher": { + "placeholder": "Publisher", + "head": "Publisher" + }, + "creativeWorkEditor": { + "placeholder": "Editor", + "head": "Editor" + }, + "creativeWorkKeywords": { + "placeholder": "Subject", + "head": "Subject" + }, + "creativeDatePublished": { + "placeholder": "Date Published", + "head": "Date Published" + }, + "organizationAddressCountry": { + "placeholder": "Country", + "head": "Country" + }, + "organizationAddressLocality": { + "placeholder": "City", + "head": "City" + }, + "organizationFoundingDate": { + "placeholder": "Date Founded", + "head": "Date Founded" } } } @@ -813,7 +844,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/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html index 37135c6036..abf5225c27 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -21,6 +21,10 @@ [fields]="['journalvolume.identifier.name']" [label]="'publication.page.volume-title'"> + +
+ + + diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts index 22e9685e89..13b7873ee6 100644 --- a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts +++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts @@ -47,9 +47,9 @@ describe('RelatedEntitiesSearchComponent', () => { expect(comp.fixedFilter).toEqual(mockFilter); }); - it('should create a fixedFilter$', () => { - comp.fixedFilter$.subscribe((fixedFilter) => { - expect(fixedFilter).toEqual(mockRelationEntityType); + it('should create a configuration$', () => { + comp.configuration$.subscribe((configuration) => { + expect(configuration).toEqual(mockRelationEntityType); }) }); diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts index 458567056b..63982c2a82 100644 --- a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts +++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts @@ -47,7 +47,7 @@ export class RelatedEntitiesSearchComponent implements OnInit { @Input() sideBarWidth = 4; fixedFilter: string; - fixedFilter$: Observable; + configuration$: Observable; constructor(private fixedFilterService: SearchFixedFilterService) { } @@ -57,7 +57,7 @@ export class RelatedEntitiesSearchComponent implements OnInit { this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id); } if (isNotEmpty(this.relationEntityType)) { - this.fixedFilter$ = of(this.relationEntityType); + this.configuration$ = of(this.relationEntityType); } } diff --git a/src/app/+search-page/configuration-search-page.component.spec.ts b/src/app/+search-page/configuration-search-page.component.spec.ts new file mode 100644 index 0000000000..a18dd38f78 --- /dev/null +++ b/src/app/+search-page/configuration-search-page.component.spec.ts @@ -0,0 +1,21 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { configureSearchComponentTestingModule } from './search-page.component.spec'; +import { SearchConfigurationService } from './search-service/search-configuration.service'; +import { ConfigurationSearchPageComponent } from './configuration-search-page.component'; + +describe('ConfigurationSearchPageComponent', () => { + let comp: ConfigurationSearchPageComponent; + let fixture: ComponentFixture; + let searchConfigService: SearchConfigurationService; + + beforeEach(async(() => { + configureSearchComponentTestingModule(ConfigurationSearchPageComponent); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigurationSearchPageComponent); + comp = fixture.componentInstance; + searchConfigService = (comp as any).searchConfigService; + fixture.detectChanges(); + }); +}); diff --git a/src/app/+search-page/configuration-search-page.component.ts b/src/app/+search-page/configuration-search-page.component.ts new file mode 100644 index 0000000000..85619e8f04 --- /dev/null +++ b/src/app/+search-page/configuration-search-page.component.ts @@ -0,0 +1,71 @@ +import { HostWindowService } from '../shared/host-window.service'; +import { SearchService } from './search-service/search.service'; +import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; +import { SearchPageComponent } from './search-page.component'; +import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core'; +import { pushInOut } from '../shared/animations/push'; +import { RouteService } from '../shared/services/route.service'; +import { SearchConfigurationService } from './search-service/search-configuration.service'; +import { Observable } from 'rxjs'; +import { PaginatedSearchOptions } from './paginated-search-options.model'; +import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component'; +import { map } from 'rxjs/operators'; + +/** + * This component renders a search page using a configuration as input. + */ +@Component({ + selector: 'ds-configuration-search-page', + styleUrls: ['./search-page.component.scss'], + templateUrl: './search-page.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + animations: [pushInOut], + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationService + } + ] +}) + +export class ConfigurationSearchPageComponent extends SearchPageComponent implements OnInit { + /** + * The configuration to use for the search options + * If empty, the configuration will be determined by the route parameter called 'configuration' + */ + @Input() configuration: string; + + constructor(protected service: SearchService, + protected sidebarService: SearchSidebarService, + protected windowService: HostWindowService, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, + protected routeService: RouteService) { + super(service, sidebarService, windowService, searchConfigService, routeService); + } + + /** + * Listening to changes in the paginated search options + * If something changes, update the search results + * + * Listen to changes in the scope + * If something changes, update the list of scopes for the dropdown + */ + ngOnInit(): void { + super.ngOnInit(); + } + + /** + * Get the current paginated search options after updating the configuration using the configuration input + * This is to make sure the configuration is included in the paginated search options, as it is not part of any + * query or route parameters + * @returns {Observable} + */ + protected getSearchOptions(): Observable { + return this.searchConfigService.paginatedSearchOptions.pipe( + map((options: PaginatedSearchOptions) => { + const config = this.configuration || options.configuration; + return Object.assign(options, { configuration: config }); + }) + ); + } +} diff --git a/src/app/+search-page/configuration-search-page.guard.ts b/src/app/+search-page/configuration-search-page.guard.ts new file mode 100644 index 0000000000..c52a0a6d8e --- /dev/null +++ b/src/app/+search-page/configuration-search-page.guard.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; + +@Injectable() +/** + * Assemble the correct i18n key for the configuration search page's title depending on the current route's configuration parameter. + * The format of the key will be "{configuration}.search.title" with: + * - configuration: The current configuration stored in route.params + */ +export class ConfigurationSearchPageGuard implements CanActivate { + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean { + const configuration = route.params.configuration; + + const newTitle = configuration + '.search.title'; + + route.data = { title: newTitle }; + return true; + } +} diff --git a/src/app/+search-page/filtered-search-page.guard.ts b/src/app/+search-page/filtered-search-page.guard.ts deleted file mode 100644 index 6d41d4965d..0000000000 --- a/src/app/+search-page/filtered-search-page.guard.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Injectable } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { Observable } from 'rxjs'; - -@Injectable() -/** - * Assemble the correct i18n key for the filtered search page's title depending on the current route's filter parameter. - * The format of the key will be "{filter}.search.title" with: - * - filter: The current filter stored in route.params - */ -export class FilteredSearchPageGuard implements CanActivate { - canActivate( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot): Observable | Promise | boolean { - const filter = route.params.filter; - - const newTitle = filter + '.search.title'; - - route.data = { title: newTitle }; - return true; - } -} diff --git a/src/app/+search-page/search-page-routing.module.ts b/src/app/+search-page/search-page-routing.module.ts index c3cf4e1343..d1ab02945e 100644 --- a/src/app/+search-page/search-page-routing.module.ts +++ b/src/app/+search-page/search-page-routing.module.ts @@ -2,14 +2,14 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { SearchPageComponent } from './search-page.component'; -import { FilteredSearchPageComponent } from './filtered-search-page.component'; -import { FilteredSearchPageGuard } from './filtered-search-page.guard'; +import { ConfigurationSearchPageGuard } from './configuration-search-page.guard'; +import { ConfigurationSearchPageComponent } from './configuration-search-page.component'; @NgModule({ imports: [ RouterModule.forChild([ { path: '', component: SearchPageComponent, data: { title: 'search.title' } }, - { path: ':filter', component: FilteredSearchPageComponent, canActivate: [FilteredSearchPageGuard]} + { path: ':configuration', component: ConfigurationSearchPageComponent, canActivate: [ConfigurationSearchPageGuard]} ]) ] }) diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index b4d8c70f11..ea04a2b04e 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -33,7 +33,7 @@
diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index af94645438..fa19665bab 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -88,10 +88,10 @@ export class SearchPageComponent implements OnInit { sideBarWidth = 3; /** - * The currently applied filter (determines title of search) + * The currently applied configuration (determines title of search) */ @Input() - fixedFilter$: Observable; + configuration$: Observable; constructor(protected service: SearchService, protected sidebarService: SearchSidebarService, @@ -119,8 +119,8 @@ export class SearchPageComponent implements OnInit { this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( switchMap((scopeId) => this.service.getScopes(scopeId)) ); - if (!isNotEmpty(this.fixedFilter$)) { - this.fixedFilter$ = this.routeService.getRouteParameterValue('filter'); + if (!isNotEmpty(this.configuration$)) { + this.configuration$ = this.routeService.getRouteParameterValue('configuration'); } } diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index f258578535..ed5c26ac56 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -9,17 +9,17 @@ import { of as observableOf, Subscription } from 'rxjs'; -import { filter, flatMap, map, switchMap, tap } from 'rxjs/operators'; -import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; +import { filter, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators'; +import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SearchOptions } from '../../../shared/search/search-options.model'; import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; import { RouteService } from '../../../shared/services/route.service'; import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util'; -import { RemoteData } from '../../data/remote-data'; -import { getSucceededRemoteData } from '../operators'; +import { RemoteData } from '../../core/data/remote-data'; +import { getSucceededRemoteData } from '../../core/shared/operators'; import { SearchFilter } from '../../../shared/search/search-filter.model'; -import { DSpaceObjectType } from '../dspace-object-type.model'; +import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; import { SearchFixedFilterService } from './search-fixed-filter.service'; /** @@ -109,9 +109,14 @@ export class SearchConfigurationService implements OnDestroy { * @returns {Observable} Emits the current configuration string */ getCurrentConfiguration(defaultConfiguration: string) { - return this.routeService.getQueryParameterValue('configuration').pipe(map((configuration) => { - return configuration || defaultConfiguration; - })); + return observableCombineLatest( + this.routeService.getQueryParameterValue('configuration').pipe(startWith(undefined)), + this.routeService.getRouteParameterValue('configuration').pipe(startWith(undefined)) + ).pipe( + map(([queryConfig, routeConfig]) => { + return queryConfig || routeConfig || defaultConfiguration; + }) + ); } /** diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.html index 65a10ec1b7..030a26df39 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.html @@ -4,14 +4,14 @@ [innerHTML]="firstMetadataValue('dc.title')"> - - + - - + - diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts index c5757f3d51..24498088cb 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts @@ -20,13 +20,13 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { value: 'This is just another title' } ], - 'journalvolume.identifier.volume': [ + 'publicationvolume.volumeNumber': [ { language: 'en_US', value: '1234' } ], - 'journalissue.identifier.number': [ + 'publicationissue.issueNumber': [ { language: 'en_US', value: '5678' diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.html index 7d7f0cf731..4e6e34d3d6 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.html @@ -10,9 +10,9 @@ - - + () diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts index 6ea5c80a5f..15f5424960 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts @@ -26,7 +26,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { value: 'This is just another journal title' } ], - 'journalvolume.identifier.volume': [ + 'publicationvolume.volumeNumber': [ { language: 'en_US', value: '1234' diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.html index 32c8074503..0e46e921bb 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.html @@ -4,9 +4,9 @@ [innerHTML]="firstMetadataValue('dc.title')"> - - + diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts index ff419148c6..204672dfe9 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts @@ -20,7 +20,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { value: 'This is just another title' } ], - 'journal.identifier.issn': [ + 'creativeworkseries.issn': [ { language: 'en_US', value: '1234' diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index 5d96abb82b..eebd3e03c8 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -7,11 +7,15 @@ + + @@ -34,11 +38,11 @@ [label]="'relationships.isPublicationOfJournalIssue' | translate">
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.spec.ts b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.spec.ts index 0711a67492..7c4f4349c3 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.spec.ts @@ -12,25 +12,25 @@ import { const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: { - 'journalissue.identifier.number': [ + 'publicationissue.issueNumber': [ { language: 'en_US', value: '1234' } ], - 'journalissue.issuedate': [ + 'creativework.datePublished': [ { language: 'en_US', value: '2018' } ], - 'journalissue.identifier.description': [ + 'dc.description': [ { language: 'en_US', value: 'desc' } ], - 'journalissue.identifier.keyword': [ + 'creativework.keywords': [ { language: 'en_US', value: 'keyword' diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index 18bf1701fc..83626c7ae7 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -7,11 +7,11 @@
@@ -25,7 +25,7 @@ [label]="'relationships.isIssueOf' | translate">
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.spec.ts b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.spec.ts index 3beca0c17a..e5da5675f4 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.spec.ts @@ -12,19 +12,19 @@ import { const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: { - 'journalvolume.identifier.volume': [ + 'publicationvolume.volumeNumber': [ { language: 'en_US', value: '1234' } ], - 'journalvolume.issuedate': [ + 'creativework.datePublished': [ { language: 'en_US', value: '2018' } ], - 'journalvolume.identifier.description': [ + 'dc.description': [ { language: 'en_US', value: 'desc' diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index 2ab3430256..a82d3c5df6 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -7,15 +7,15 @@
@@ -25,7 +25,7 @@ [label]="'relationships.isVolumeOf' | translate">
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts index 4189713cb9..9690c74966 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts @@ -22,19 +22,19 @@ let fixture: ComponentFixture; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: { - 'journal.identifier.issn': [ + 'creativeworkseries.issn': [ { language: 'en_US', value: '1234' } ], - 'journal.publisher': [ + 'creativework.publisher': [ { language: 'en_US', value: 'a publisher' } ], - 'journal.identifier.description': [ + 'dc.description': [ { language: 'en_US', value: 'desc' diff --git a/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.html index a809c0f655..8d312fb7c0 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.html @@ -1,13 +1,13 @@ + [innerHTML]="firstMetadataValue('organization.legalName')"> - + [innerHTML]="firstMetadataValue('dc.description')"> diff --git a/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.spec.ts index ef5d7a0b4e..dd2b138abb 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-list-element.component.spec.ts @@ -20,7 +20,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { value: 'This is just another title' } ], - 'orgunit.identifier.description': [ + 'dc.description': [ { language: 'en_US', value: 'A description about the OrgUnit' diff --git a/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-metadata-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-metadata-list-element.component.html index 463770c0ae..ea429e87c6 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-metadata-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/orgunit/orgunit-metadata-list-element.component.html @@ -1,13 +1,13 @@ - - + diff --git a/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.html index 52b69453ce..c88b77083d 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.html @@ -1,12 +1,12 @@ + [innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"> - - + diff --git a/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts index 7c5240da95..3b6aeae45b 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts @@ -20,7 +20,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { value: 'This is just another title' } ], - 'person.identifier.jobtitle': [ + 'person.jobTitle': [ { language: 'en_US', value: 'Developer' diff --git a/src/app/entity-groups/research-entities/item-list-elements/person/person-metadata-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/person/person-metadata-list-element.component.html index 3dfe17debc..1125c2fb9b 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/person/person-metadata-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/person/person-metadata-list-element.component.html @@ -1,8 +1,8 @@ - - + @@ -10,6 +10,6 @@ diff --git a/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.html index 6f0faa90ef..3e979b4e4d 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.html @@ -1,16 +1,16 @@ - - - - - - - - - + [innerHTML]="firstMetadataValue('dc.title')"> + + + + + + + + + + diff --git a/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts index f74d7931b0..02dc3f6d73 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts @@ -20,12 +20,12 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { value: 'This is just another title' } ], - 'project.identifier.status': [ - { - language: 'en_US', - value: 'A status about the project' - } - ] + // 'project.identifier.status': [ + // { + // language: 'en_US', + // value: 'A status about the project' + // } + // ] } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { @@ -61,27 +61,27 @@ describe('ProjectListElementComponent', () => { })); - describe('When the item has a status', () => { - beforeEach(() => { - projectListElementComponent.item = mockItemWithMetadata; - fixture.detectChanges(); - }); - - it('should show the status span', () => { - const statusField = fixture.debugElement.query(By.css('span.item-list-status')); - expect(statusField).not.toBeNull(); - }); - }); - - describe('When the item has no status', () => { - beforeEach(() => { - projectListElementComponent.item = mockItemWithoutMetadata; - fixture.detectChanges(); - }); - - it('should not show the status span', () => { - const statusField = fixture.debugElement.query(By.css('span.item-list-status')); - expect(statusField).toBeNull(); - }); - }); + // describe('When the item has a status', () => { + // beforeEach(() => { + // projectListElementComponent.item = mockItemWithMetadata; + // fixture.detectChanges(); + // }); + // + // it('should show the status span', () => { + // const statusField = fixture.debugElement.query(By.css('span.item-list-status')); + // expect(statusField).not.toBeNull(); + // }); + // }); + // + // describe('When the item has no status', () => { + // beforeEach(() => { + // projectListElementComponent.item = mockItemWithoutMetadata; + // fixture.detectChanges(); + // }); + // + // it('should not show the status span', () => { + // const statusField = fixture.debugElement.query(By.css('span.item-list-status')); + // expect(statusField).toBeNull(); + // }); + // }); }); diff --git a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html index 0446ac6861..92ac3eac30 100644 --- a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html @@ -1,5 +1,5 @@

- {{'orgunit.page.titleprefix' | translate}} + {{'orgunit.page.titleprefix' | translate}}

@@ -7,19 +7,19 @@
@@ -37,7 +37,7 @@ [label]="'relationships.isPublicationOf' | translate">
diff --git a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.spec.ts b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.spec.ts index 52caf69d72..b415879df3 100644 --- a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.spec.ts @@ -12,31 +12,31 @@ import { const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: { - 'orgunit.identifier.dateestablished': [ + 'organization.foundingDate': [ { language: 'en_US', value: '2018' } ], - 'orgunit.identifier.city': [ + 'organization.address.addressLocality': [ { language: 'en_US', value: 'New York' } ], - 'orgunit.identifier.country': [ + 'organization.adress.addressCountry': [ { language: 'en_US', value: 'USA' } ], - 'orgunit.identifier.id': [ + 'dc.identifier': [ { language: 'en_US', value: '1' } ], - 'orgunit.identifier.description': [ + 'dc.description': [ { language: 'en_US', value: 'desc' diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index 88cd647645..04d7b9e062 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -1,5 +1,5 @@

- {{'person.page.titleprefix' | translate}} + {{'person.page.titleprefix' | translate}}

@@ -7,21 +7,21 @@ + + + + - - - - + + + +
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts index beadbbef79..73f61983fc 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.spec.ts @@ -12,43 +12,43 @@ import { const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: { - 'person.identifier.email': [ + 'person.email': [ { language: 'en_US', value: 'fake@email.com' } ], - 'person.identifier.orcid': [ - { - language: 'en_US', - value: 'ORCID-1' - } - ], - 'person.identifier.birthdate': [ + // 'person.identifier.orcid': [ + // { + // language: 'en_US', + // value: 'ORCID-1' + // } + // ], + 'person.birthDate': [ { language: 'en_US', value: '1993' } ], - 'person.identifier.staffid': [ - { - language: 'en_US', - value: '1' - } - ], - 'person.identifier.jobtitle': [ + // 'person.identifier.staffid': [ + // { + // language: 'en_US', + // value: '1' + // } + // ], + 'person.jobTitle': [ { language: 'en_US', value: 'Developer' } ], - 'person.identifier.lastname': [ + 'person.familyName': [ { language: 'en_US', value: 'Doe' } ], - 'person.identifier.firstname': [ + 'person.givenName': [ { language: 'en_US', value: 'John' diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index 08e386182b..4e9a130b8c 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -1,15 +1,15 @@

- {{'project.page.titleprefix' | translate}} + {{'project.page.titleprefix' | translate}}

- - + + + + @@ -19,13 +19,13 @@ [label]="'project.page.funder'"> - - + + + +
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.spec.ts b/src/app/entity-groups/research-entities/item-pages/project/project.component.spec.ts index 1e1fd42517..ad4b70e2aa 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.spec.ts @@ -12,31 +12,31 @@ import { const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: { - 'project.identifier.status': [ - { - language: 'en_US', - value: 'published' - } - ], - 'project.identifier.id': [ + // 'project.identifier.status': [ + // { + // language: 'en_US', + // value: 'published' + // } + // ], + 'dc.identifier': [ { language: 'en_US', value: '1' } ], - 'project.identifier.expectedcompletion': [ - { - language: 'en_US', - value: 'exp comp' - } - ], - 'project.identifier.description': [ + // 'project.identifier.expectedcompletion': [ + // { + // language: 'en_US', + // value: 'exp comp' + // } + // ], + 'dc.description': [ { language: 'en_US', value: 'keyword' } ], - 'project.identifier.keyword': [ + 'dc.subject': [ { language: 'en_US', value: 'keyword' 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/app/shared/search/search-result-element-decorator.ts b/src/app/shared/search/search-result-element-decorator.ts index 446236c1b7..8894907765 100644 --- a/src/app/shared/search/search-result-element-decorator.ts +++ b/src/app/shared/search/search-result-element-decorator.ts @@ -1,6 +1,6 @@ import { GenericConstructor } from '../../core/shared/generic-constructor'; import { ListableObject } from '../object-collection/shared/listable-object.model'; -import { isNull } from '../empty.util'; +import { hasNoValue, isNull } from '../empty.util'; /** * Contains the mapping between a search result component and a DSpaceObject @@ -34,7 +34,7 @@ export function searchResultFor(domainConstructor: GenericConstructor, configuration: string = null) { - if (isNull(configuration) || configuration === 'default') { + if (isNull(configuration) || configuration === 'default' || hasNoValue(searchResultMap.get(configuration))) { return searchResultMap.get(domainConstructor); } else { return searchResultMap.get(configuration).get(domainConstructor); diff --git a/src/app/shared/search/search-results/search-results.component.ts b/src/app/shared/search/search-results/search-results.component.ts index 21222e9241..09178e9cad 100644 --- a/src/app/shared/search/search-results/search-results.component.ts +++ b/src/app/shared/search/search-results/search-results.component.ts @@ -45,9 +45,9 @@ export class SearchResultsComponent { @Input() viewMode: SetViewMode; /** - * An optional fixed filter to filter the result on one type + * An optional configuration to filter the result on one type */ - @Input() fixedFilter: string; + @Input() configuration: string; /** * Whether or not to hide the header of the results @@ -58,19 +58,6 @@ export class SearchResultsComponent { @Input() selectable = false; @Input() selectionConfig: {repeatable: boolean, listId: string}; - /** - * Get the i18n key for the title depending on the fixed filter - * Defaults to 'search.results.head' if there's no fixed filter found - * @returns {string} - */ - getTitleKey() { - if (isNotEmpty(this.fixedFilter)) { - return this.fixedFilter + '.search.results.head' - } else { - return 'search.results.head'; - } - } - /** * Method to change the given string by surrounding it by quotes if not already present. */ 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; +}