1
0

108588: Passed the scope to the browse sections

This commit is contained in:
Alexandre Vryghem
2023-12-13 01:19:46 +01:00
parent d9759d1136
commit d95f9d0140
10 changed files with 114 additions and 67 deletions

View File

@@ -61,16 +61,16 @@ export class BrowseByDateComponent extends BrowseByMetadataComponent implements
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig);
this.subs.push(
observableCombineLatest([this.route.params, this.route.queryParams, this.route.data,
observableCombineLatest([this.route.params, this.route.queryParams, this.scope$, this.route.data,
this.currentPagination$, this.currentSort$]).pipe(
map(([routeParams, queryParams, data, currentPage, currentSort]) => {
return [Object.assign({}, routeParams, queryParams, data), currentPage, currentSort];
map(([routeParams, queryParams, scope, data, currentPage, currentSort]) => {
return [Object.assign({}, routeParams, queryParams, data), scope, currentPage, currentSort];
})
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
).subscribe(([params, scope, currentPage, currentSort]: [Params, string, PaginationComponentOptions, SortOptions]) => {
const metadataKeys = params.browseDefinition ? params.browseDefinition.metadataKeys : this.defaultMetadataKeys;
this.browseId = params.id || this.defaultBrowseId;
this.startsWith = +params.startsWith || params.startsWith;
const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails);
const searchOptions = browseParamsToOptions(params, scope, currentPage, currentSort, this.browseId, this.fetchThumbnails);
this.updatePageWithItems(searchOptions, this.value, undefined);
this.updateStartsWithOptions(this.browseId, metadataKeys, params.scope);
}));

View File

@@ -1,17 +1,13 @@
import { first } from 'rxjs/operators';
import { BrowseByGuard } from './browse-by-guard';
import { of as observableOf } from 'rxjs';
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type';
import { ValueListBrowseDefinition } from '../core/shared/value-list-browse-definition.model';
import { DSONameServiceMock } from '../shared/mocks/dso-name.service.mock';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
import { RouterStub } from '../shared/testing/router.stub';
describe('BrowseByGuard', () => {
describe('canActivate', () => {
let guard: BrowseByGuard;
let dsoService: any;
let translateService: any;
let browseDefinitionService: any;
let router: any;
@@ -25,10 +21,6 @@ describe('BrowseByGuard', () => {
const browseDefinition = Object.assign(new ValueListBrowseDefinition(), { type: BrowseByDataType.Metadata, metadataKeys: ['dc.contributor'] });
beforeEach(() => {
dsoService = {
findById: (dsoId: string) => observableOf({ payload: { name: name }, hasSucceeded: true })
};
translateService = {
instant: () => field
};
@@ -39,7 +31,7 @@ describe('BrowseByGuard', () => {
router = new RouterStub() as any;
guard = new BrowseByGuard(dsoService, translateService, browseDefinitionService, new DSONameServiceMock() as DSONameService, router);
guard = new BrowseByGuard(translateService, browseDefinitionService, router);
});
it('should return true, and sets up the data correctly, with a scope and value', () => {
@@ -48,6 +40,7 @@ describe('BrowseByGuard', () => {
title: field,
browseDefinition,
},
parent: null,
params: {
id,
},
@@ -64,7 +57,7 @@ describe('BrowseByGuard', () => {
title,
id,
browseDefinition,
collection: name,
scope,
field,
value: '"' + value + '"'
};
@@ -97,7 +90,7 @@ describe('BrowseByGuard', () => {
title,
id,
browseDefinition,
collection: name,
scope,
field,
value: ''
};
@@ -108,12 +101,48 @@ describe('BrowseByGuard', () => {
);
});
it('should return true, and sets up the data correctly using the community/collection page id, with a scope and without value', () => {
const scopedNoValueRoute = {
data: {
title: field,
browseDefinition,
},
parent: {
params: {
id: scope,
},
},
params: {
id,
},
queryParams: {
},
};
guard.canActivate(scopedNoValueRoute as any, undefined).pipe(
first(),
).subscribe((canActivate) => {
const result = {
title,
id,
browseDefinition,
scope,
field,
value: '',
};
expect(scopedNoValueRoute.data).toEqual(result);
expect(router.navigate).not.toHaveBeenCalled();
expect(canActivate).toEqual(true);
});
});
it('should return true, and sets up the data correctly, without a scope and with a value', () => {
const route = {
data: {
title: field,
browseDefinition,
},
parent: null,
params: {
id,
},
@@ -129,7 +158,7 @@ describe('BrowseByGuard', () => {
title,
id,
browseDefinition,
collection: '',
scope: undefined,
field,
value: '"' + value + '"'
};
@@ -147,6 +176,7 @@ describe('BrowseByGuard', () => {
data: {
title: field,
},
parent: null,
params: {
id,
},

View File

@@ -1,15 +1,12 @@
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, Data } from '@angular/router';
import { Injectable } from '@angular/core';
import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service';
import { hasNoValue, hasValue } from '../shared/empty.util';
import { map, switchMap } from 'rxjs/operators';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators';
import { getFirstCompletedRemoteData } from '../core/shared/operators';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of as observableOf } from 'rxjs';
import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service';
import { BrowseDefinition } from '../core/shared/browse-definition.model';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { RemoteData } from '../core/data/remote-data';
import { PAGE_NOT_FOUND_PATH } from '../app-routing-paths';
@@ -19,11 +16,10 @@ import { PAGE_NOT_FOUND_PATH } from '../app-routing-paths';
*/
export class BrowseByGuard implements CanActivate {
constructor(protected dsoService: DSpaceObjectDataService,
protected translate: TranslateService,
protected browseDefinitionService: BrowseDefinitionDataService,
protected dsoNameService: DSONameService,
protected router: Router,
constructor(
protected translate: TranslateService,
protected browseDefinitionService: BrowseDefinitionDataService,
protected router: Router,
) {
}
@@ -39,25 +35,14 @@ export class BrowseByGuard implements CanActivate {
} else {
browseDefinition$ = observableOf(route.data.browseDefinition);
}
const scope = route.queryParams.scope;
const scope = route.queryParams.scope ?? route.parent?.params.id;
const value = route.queryParams.value;
const metadataTranslated = this.translate.instant(`browse.metadata.${id}`);
return browseDefinition$.pipe(
switchMap((browseDefinition: BrowseDefinition | undefined) => {
if (hasValue(browseDefinition)) {
if (hasValue(scope)) {
const dso$: Observable<DSpaceObject> = this.dsoService.findById(scope).pipe(getFirstSucceededRemoteDataPayload());
return dso$.pipe(
map((dso: DSpaceObject) => {
const name = this.dsoNameService.getName(dso);
route.data = this.createData(title, id, browseDefinition, name, metadataTranslated, value, route);
return true;
})
);
} else {
route.data = this.createData(title, id, browseDefinition, '', metadataTranslated, value, route);
return observableOf(true);
}
route.data = this.createData(title, id, browseDefinition, metadataTranslated, value, route, scope);
return observableOf(true);
} else {
void this.router.navigate([PAGE_NOT_FOUND_PATH]);
return observableOf(false);
@@ -66,14 +51,14 @@ export class BrowseByGuard implements CanActivate {
);
}
private createData(title, id, browseDefinition, collection, field, value, route) {
private createData(title: string, id: string, browseDefinition: BrowseDefinition, field: string, value: string, route: ActivatedRouteSnapshot, scope: string): Data {
return Object.assign({}, route.data, {
title: title,
id: id,
browseDefinition: browseDefinition,
collection: collection,
field: field,
value: hasValue(value) ? `"${value}"` : ''
value: hasValue(value) ? `"${value}"` : '',
scope: scope,
});
}
}

View File

@@ -170,7 +170,7 @@ describe('BrowseByMetadataComponent', () => {
field: 'fake-field',
};
result = browseParamsToOptions(paramsScope, paginationOptions, sortOptions, 'author', comp.fetchThumbnails);
result = browseParamsToOptions(paramsScope, 'fake-scope', paginationOptions, sortOptions, 'author', comp.fetchThumbnails);
});
it('should return BrowseEntrySearchOptions with the correct properties', () => {

View File

@@ -1,5 +1,5 @@
import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
import { Component, Inject, OnInit, OnDestroy, Input } from '@angular/core';
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
import { Component, Inject, OnInit, OnDestroy, Input, OnChanges } from '@angular/core';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
@@ -35,7 +35,7 @@ export const BBM_PAGINATION_ID = 'bbm';
* 'dc.contributor.*'
*/
@rendersBrowseBy(BrowseByDataType.Metadata)
export class BrowseByMetadataComponent implements OnInit, OnDestroy {
export class BrowseByMetadataComponent implements OnInit, OnChanges, OnDestroy {
/**
* The optional context
@@ -47,6 +47,13 @@ export class BrowseByMetadataComponent implements OnInit, OnDestroy {
*/
@Input() browseByType: BrowseByDataType;
/**
* The ID of the {@link Community} or {@link Collection} of the scope to display
*/
@Input() scope: string;
scope$: BehaviorSubject<string> = new BehaviorSubject(undefined);
/**
* The list of browse-entries to display
*/
@@ -145,12 +152,12 @@ export class BrowseByMetadataComponent implements OnInit, OnDestroy {
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig);
this.subs.push(
observableCombineLatest([this.route.params, this.route.queryParams, this.currentPagination$, this.currentSort$]).pipe(
map(([routeParams, queryParams, currentPage, currentSort]) => {
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
observableCombineLatest([this.route.params, this.route.queryParams, this.scope$, this.currentPagination$, this.currentSort$]).pipe(
map(([routeParams, queryParams, scope, currentPage, currentSort]) => {
return [Object.assign({}, routeParams, queryParams), scope, currentPage, currentSort];
})
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
this.browseId = params.id || this.defaultBrowseId;
).subscribe(([params, scope, currentPage, currentSort]: [Params, string, PaginationComponentOptions, SortOptions]) => {
this.browseId = params.id || this.defaultBrowseId;
this.authority = params.authority;
if (typeof params.value === 'string'){
@@ -164,16 +171,19 @@ export class BrowseByMetadataComponent implements OnInit, OnDestroy {
}
if (isNotEmpty(this.value)) {
this.updatePageWithItems(
browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails), this.value, this.authority);
this.updatePageWithItems(browseParamsToOptions(params, scope, currentPage, currentSort, this.browseId, this.fetchThumbnails), this.value, this.authority);
} else {
this.updatePage(browseParamsToOptions(params, currentPage, currentSort, this.browseId, false));
this.updatePage(browseParamsToOptions(params, scope, currentPage, currentSort, this.browseId, false));
}
}));
this.updateStartsWithTextOptions();
}
ngOnChanges(): void {
this.scope$.next(this.scope);
}
/**
* Update the StartsWith options with text values
* It adds the value "0-9" as well as all letters from A to Z
@@ -268,12 +278,14 @@ export function getBrowseSearchOptions(defaultBrowseId: string,
/**
* Function to transform query and url parameters into searchOptions used to fetch browse entries or items
* @param params URL and query parameters
* @param scope The scope to show the results
* @param paginationConfig Pagination configuration
* @param sortConfig Sorting configuration
* @param metadata Optional metadata definition to fetch browse entries/items for
* @param fetchThumbnail Optional parameter for requesting thumbnail images
*/
export function browseParamsToOptions(params: any,
scope: string,
paginationConfig: PaginationComponentOptions,
sortConfig: SortOptions,
metadata?: string,
@@ -283,7 +295,7 @@ export function browseParamsToOptions(params: any,
paginationConfig,
sortConfig,
params.startsWith,
params.scope,
scope,
fetchThumbnail
);
}

View File

@@ -15,6 +15,8 @@ export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent<
@Input() browseByType: BrowseByDataType;
@Input() scope: string;
protected inputNamesDependentForComponent: (keyof this & string)[] = [
'context',
'browseByType',
@@ -23,6 +25,7 @@ export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent<
protected inputNames: (keyof this & string)[] = [
'context',
'browseByType',
'scope',
];
public getComponent(): GenericConstructor<Component> {

View File

@@ -1,8 +1,8 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { Component, OnInit, OnChanges, OnDestroy, Input } from '@angular/core';
import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model';
import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { ActivatedRoute } from '@angular/router';
import { Observable, Subscription } from 'rxjs';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { BrowseDefinition } from '../../core/shared/browse-definition.model';
import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator';
import { map } from 'rxjs/operators';
@@ -19,7 +19,7 @@ import { Context } from '../../core/shared/context.model';
* Component for browsing items by metadata in a hierarchical controlled vocabulary
*/
@rendersBrowseBy(BrowseByDataType.Hierarchy)
export class BrowseByTaxonomyComponent implements OnInit, OnDestroy {
export class BrowseByTaxonomyComponent implements OnInit, OnChanges, OnDestroy {
/**
* The optional context
@@ -31,6 +31,13 @@ export class BrowseByTaxonomyComponent implements OnInit, OnDestroy {
*/
@Input() browseByType: BrowseByDataType;
/**
* The ID of the {@link Community} or {@link Collection} of the scope to display
*/
@Input() scope: string;
scope$: BehaviorSubject<string> = new BehaviorSubject(undefined);
/**
* The {@link VocabularyOptions} object
*/
@@ -89,6 +96,10 @@ export class BrowseByTaxonomyComponent implements OnInit, OnDestroy {
}));
}
ngOnChanges(): void {
this.scope$.next(this.scope);
}
/**
* Adds detail to selectedItems, transforms it to be used as query parameter
* and adds that to filterValues.

View File

@@ -29,14 +29,14 @@ export class BrowseByTitleComponent extends BrowseByMetadataComponent implements
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig);
this.subs.push(
observableCombineLatest([this.route.params, this.route.queryParams, this.currentPagination$, this.currentSort$]).pipe(
map(([routeParams, queryParams, currentPage, currentSort]) => {
return [Object.assign({}, routeParams, queryParams), currentPage, currentSort];
observableCombineLatest([this.route.params, this.route.queryParams, this.scope$, this.currentPagination$, this.currentSort$]).pipe(
map(([routeParams, queryParams, scope, currentPage, currentSort]) => {
return [Object.assign({}, routeParams, queryParams), scope, currentPage, currentSort];
})
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
).subscribe(([params, scope, currentPage, currentSort]: [Params, string, PaginationComponentOptions, SortOptions]) => {
this.startsWith = +params.startsWith || params.startsWith;
this.browseId = params.id || this.defaultBrowseId;
this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails), undefined, undefined);
this.updatePageWithItems(browseParamsToOptions(params, scope, currentPage, currentSort, this.browseId, this.fetchThumbnails), undefined, undefined);
}));
this.updateStartsWithTextOptions();
}

View File

@@ -1,2 +1,3 @@
<ds-browse-by-switcher [browseByType]="browseByType$ | async">
<ds-browse-by-switcher [browseByType]="browseByType$ | async"
[scope]="scope$ | async">
</ds-browse-by-switcher>

View File

@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BrowseByDataType } from '../../../../browse-by/browse-by-switcher/browse-by-data-type';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Data } from '@angular/router';
import { map } from 'rxjs/operators';
import { BrowseDefinition } from '../../../../core/shared/browse-definition.model';
@@ -14,6 +14,8 @@ export class ComcolBrowseByComponent implements OnInit {
browseByType$: Observable<BrowseByDataType>;
scope$: Observable<string>;
constructor(
protected route: ActivatedRoute,
) {
@@ -26,6 +28,9 @@ export class ComcolBrowseByComponent implements OnInit {
this.browseByType$ = this.route.data.pipe(
map((data: { browseDefinition: BrowseDefinition }) => data.browseDefinition.getRenderType()),
);
this.scope$ = this.route.data.pipe(
map((data: Data) => data.scope),
);
}
}