From b709ee03000086cd52f5de9bc67d5619a64df29d Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 11 Oct 2024 14:59:34 +0200 Subject: [PATCH] 118944: Embed the accessStatus link on collection, browse, recent item submissions & the item page related items & delete tab when enabled --- .../collection-page.component.ts | 4 +-- .../breadcrumbs/item-breadcrumb.resolver.ts | 4 +-- src/app/core/browse/browse.service.spec.ts | 12 -------- src/app/core/browse/browse.service.ts | 19 ++++++++----- .../data/relationship-data.service.spec.ts | 4 ++- .../core/data/relationship-data.service.ts | 4 ++- .../recent-item-list.component.ts | 3 ++ .../abstract-item-update.component.ts | 4 +-- .../item-delete/item-delete.component.ts | 4 +-- .../edit-relationship-list.component.spec.ts | 5 ++-- .../edit-relationship-list.component.ts | 2 +- src/app/item-page/item.resolver.ts | 27 +++++++++++------- ...ynamic-form-control-container.component.ts | 2 +- src/app/shared/utils/relation-query.utils.ts | 28 +++++++++++-------- 14 files changed, 67 insertions(+), 55 deletions(-) diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 16704cef52e..e74db09f248 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -28,7 +28,7 @@ import { AuthorizationDataService } from '../core/data/feature-authorization/aut import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCollectionPageRoute } from './collection-page-routing-paths'; import { redirectOn4xx } from '../core/shared/authorized.operators'; -import { BROWSE_LINKS_TO_FOLLOW } from '../core/browse/browse.service'; +import { getBrowseLinksToFollow } from '../core/browse/browse.service'; import { DSONameService } from '../core/breadcrumbs/dso-name.service'; import { APP_CONFIG, AppConfig } from '../../../src/config/app-config.interface'; @@ -115,7 +115,7 @@ export class CollectionPageComponent implements OnInit { pagination: currentPagination, sort: currentSort, dsoTypes: [DSpaceObjectType.ITEM] - }), null, true, true, ...BROWSE_LINKS_TO_FOLLOW) + }), null, true, true, ...getBrowseLinksToFollow()) .pipe(toDSpaceObjectListRD()) as Observable>>; }), startWith(undefined) // Make sure switching pages shows loading component diff --git a/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts index 3005b6f09ac..f396fd08dda 100644 --- a/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts @@ -4,7 +4,7 @@ import { ItemDataService } from '../data/item-data.service'; import { Item } from '../shared/item.model'; import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; -import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item-page/item.resolver'; +import { getItemPageLinksToFollow } from '../../item-page/item.resolver'; /** * The class that resolves the BreadcrumbConfig object for an Item @@ -23,6 +23,6 @@ export class ItemBreadcrumbResolver extends DSOBreadcrumbResolver { * Requesting them as embeds will limit the number of requests */ get followLinks(): FollowLinkConfig[] { - return ITEM_PAGE_LINKS_TO_FOLLOW; + return getItemPageLinksToFollow(); } } diff --git a/src/app/core/browse/browse.service.spec.ts b/src/app/core/browse/browse.service.spec.ts index 9f166e3d19d..f7027fc91e1 100644 --- a/src/app/core/browse/browse.service.spec.ts +++ b/src/app/core/browse/browse.service.spec.ts @@ -1,10 +1,8 @@ import { cold, getTestScheduler, hot } from 'jasmine-marbles'; import { of as observableOf } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; -import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestService } from '../data/request.service'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; import { BrowseService } from './browse.service'; @@ -20,7 +18,6 @@ describe('BrowseService', () => { let scheduler: TestScheduler; let service: BrowseService; let requestService: RequestService; - let rdbService: RemoteDataBuildService; const browsesEndpointURL = 'https://rest.api/browses'; const halService: any = new HALEndpointServiceStub(browsesEndpointURL); @@ -118,7 +115,6 @@ describe('BrowseService', () => { halService, browseDefinitionDataService, hrefOnlyDataService, - rdbService ); } @@ -130,11 +126,9 @@ describe('BrowseService', () => { beforeEach(() => { requestService = getMockRequestService(getRequestEntry$(true)); - rdbService = getMockRemoteDataBuildService(); service = initTestService(); spyOn(halService, 'getEndpoint').and .returnValue(hot('--a-', { a: browsesEndpointURL })); - spyOn(rdbService, 'buildList').and.callThrough(); }); it('should call BrowseDefinitionDataService to create the RemoteData Observable', () => { @@ -151,9 +145,7 @@ describe('BrowseService', () => { beforeEach(() => { requestService = getMockRequestService(getRequestEntry$(true)); - rdbService = getMockRemoteDataBuildService(); service = initTestService(); - spyOn(rdbService, 'buildList').and.callThrough(); }); describe('when getBrowseEntriesFor is called with a valid browse definition id', () => { @@ -204,7 +196,6 @@ describe('BrowseService', () => { describe('if getBrowseDefinitions fires', () => { beforeEach(() => { requestService = getMockRequestService(getRequestEntry$(true)); - rdbService = getMockRemoteDataBuildService(); service = initTestService(); spyOn(service, 'getBrowseDefinitions').and .returnValue(hot('--a-', { @@ -259,7 +250,6 @@ describe('BrowseService', () => { describe('if getBrowseDefinitions doesn\'t fire', () => { it('should return undefined', () => { requestService = getMockRequestService(getRequestEntry$(true)); - rdbService = getMockRemoteDataBuildService(); service = initTestService(); spyOn(service, 'getBrowseDefinitions').and .returnValue(hot('----')); @@ -277,9 +267,7 @@ describe('BrowseService', () => { describe('getFirstItemFor', () => { beforeEach(() => { requestService = getMockRequestService(); - rdbService = getMockRemoteDataBuildService(); service = initTestService(); - spyOn(rdbService, 'buildList').and.callThrough(); }); describe('when getFirstItemFor is called with a valid browse definition id', () => { diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index b210b349494..2d8dd4b80f6 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map, startWith } from 'rxjs/operators'; import { hasValue, hasValueOperator, isEmpty, isNotEmpty } from '../../shared/empty.util'; -import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { PaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; import { RequestService } from '../data/request.service'; @@ -24,11 +23,18 @@ import { HrefOnlyDataService } from '../data/href-only-data.service'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { BrowseDefinitionDataService } from './browse-definition-data.service'; import { SortDirection } from '../cache/models/sort-options.model'; +import { environment } from '../../../environments/environment'; -export const BROWSE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ - followLink('thumbnail') -]; +export function getBrowseLinksToFollow(): FollowLinkConfig[] { + const followLinks = [ + followLink('thumbnail'), + ]; + if (environment.item.showAccessStatuses) { + followLinks.push(followLink('accessStatus')); + } + return followLinks; +} /** * The service handling all browse requests @@ -55,7 +61,6 @@ export class BrowseService { protected halService: HALEndpointService, private browseDefinitionDataService: BrowseDefinitionDataService, private hrefOnlyDataService: HrefOnlyDataService, - private rdb: RemoteDataBuildService, ) { } @@ -105,7 +110,7 @@ export class BrowseService { }) ); if (options.fetchThumbnail ) { - return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...BROWSE_LINKS_TO_FOLLOW); + return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...getBrowseLinksToFollow()); } return this.hrefOnlyDataService.findListByHref(href$); } @@ -153,7 +158,7 @@ export class BrowseService { }), ); if (options.fetchThumbnail) { - return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...BROWSE_LINKS_TO_FOLLOW); + return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...getBrowseLinksToFollow()); } return this.hrefOnlyDataService.findListByHref(href$); } diff --git a/src/app/core/data/relationship-data.service.spec.ts b/src/app/core/data/relationship-data.service.spec.ts index 4432d5213ae..fd24c0665bc 100644 --- a/src/app/core/data/relationship-data.service.spec.ts +++ b/src/app/core/data/relationship-data.service.spec.ts @@ -23,6 +23,7 @@ import { FindListOptions } from './find-list-options.model'; import { testSearchDataImplementation } from './base/search-data.spec'; import { MetadataValue } from '../shared/metadata.models'; import { MetadataRepresentationType } from '../shared/metadata-representation/metadata-representation.model'; +import { environment } from '../../../environments/environment.test'; describe('RelationshipDataService', () => { let service: RelationshipDataService; @@ -137,6 +138,7 @@ describe('RelationshipDataService', () => { itemService, null, jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v), + environment, ); } @@ -152,7 +154,7 @@ describe('RelationshipDataService', () => { }); describe('composition', () => { - const initService = () => new RelationshipDataService(null, null, null, null, null, null, null); + const initService = () => new RelationshipDataService(null, null, null, null, null, null, null, environment); testSearchDataImplementation(initService); }); diff --git a/src/app/core/data/relationship-data.service.ts b/src/app/core/data/relationship-data.service.ts index 46a51a2d010..7620f8ab756 100644 --- a/src/app/core/data/relationship-data.service.ts +++ b/src/app/core/data/relationship-data.service.ts @@ -51,6 +51,7 @@ import { MetadataRepresentation } from '../shared/metadata-representation/metada import { MetadatumRepresentation } from '../shared/metadata-representation/metadatum/metadatum-representation.model'; import { ItemMetadataRepresentation } from '../shared/metadata-representation/item/item-metadata-representation.model'; import { DSpaceObject } from '../shared/dspace-object.model'; +import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; @@ -92,6 +93,7 @@ export class RelationshipDataService extends IdentifiableDataService, @Inject(PAGINATED_RELATIONS_TO_ITEMS_OPERATOR) private paginatedRelationsToItems: (thisId: string) => (source: Observable>>) => Observable>>, + @Inject(APP_CONFIG) private appConfig: AppConfig, ) { super('relationships', requestService, rdbService, objectCache, halService, 15 * 60 * 1000); @@ -264,7 +266,7 @@ export class RelationshipDataService extends IdentifiableDataService>> { - let linksToFollow: FollowLinkConfig[] = itemLinksToFollow(options.fetchThumbnail); + let linksToFollow: FollowLinkConfig[] = itemLinksToFollow(options.fetchThumbnail, this.appConfig.item.showAccessStatuses); linksToFollow.push(followLink('relationshipType')); return this.getItemRelationshipsByLabel(item, label, options, true, true, ...linksToFollow).pipe(this.paginatedRelationsToItems(item.uuid)); diff --git a/src/app/home-page/recent-item-list/recent-item-list.component.ts b/src/app/home-page/recent-item-list/recent-item-list.component.ts index 9a8535d970a..da7d8141e36 100644 --- a/src/app/home-page/recent-item-list/recent-item-list.component.ts +++ b/src/app/home-page/recent-item-list/recent-item-list.component.ts @@ -64,6 +64,9 @@ export class RecentItemListComponent implements OnInit { if (this.appConfig.browseBy.showThumbnails) { linksToFollow.push(followLink('thumbnail')); } + if (this.appConfig.item.showAccessStatuses) { + linksToFollow.push(followLink('accessStatus')); + } this.itemRD$ = this.searchService.search( new PaginatedSearchOptions({ diff --git a/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts b/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts index 80002f614b6..3a076323460 100644 --- a/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts +++ b/src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts @@ -13,7 +13,7 @@ import { environment } from '../../../../environments/environment'; import { getItemPageRoute } from '../../item-page-routing-paths'; import { getAllSucceededRemoteData } from '../../../core/shared/operators'; import { hasValue } from '../../../shared/empty.util'; -import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item.resolver'; +import { getItemPageLinksToFollow } from '../../item.resolver'; import { FieldUpdate } from '../../../core/data/object-updates/field-update.model'; import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model'; @@ -72,7 +72,7 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl this.item = rd.payload; }), switchMap((rd: RemoteData) => { - return this.itemService.findByHref(rd.payload._links.self.href, true, true, ...ITEM_PAGE_LINKS_TO_FOLLOW); + return this.itemService.findByHref(rd.payload._links.self.href, true, true, ...getItemPageLinksToFollow()); }), getAllSucceededRemoteData() ).subscribe((rd: RemoteData) => { diff --git a/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts b/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts index 9012ebe7d75..8b26b71fef3 100644 --- a/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts +++ b/src/app/item-page/edit-item-page/item-delete/item-delete.component.ts @@ -236,8 +236,8 @@ export class ItemDeleteComponent this.linkService.resolveLinks( relationship, followLink('relationshipType'), - followLink('leftItem'), - followLink('rightItem'), + followLink('leftItem', undefined, followLink('accessStatus')), + followLink('rightItem', undefined, followLink('accessStatus')), ); return relationship.relationshipType.pipe( getFirstSucceededRemoteData(), diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 4cd663f0fbc..29851c9e936 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -32,6 +32,7 @@ import { ConfigurationProperty } from '../../../../core/shared/configuration-pro import { Router } from '@angular/router'; import { RouterMock } from '../../../../shared/mocks/router.mock'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; +import { environment } from '../../../../../environments/environment.test'; let comp: EditRelationshipListComponent; let fixture: ComponentFixture; @@ -202,11 +203,11 @@ describe('EditRelationshipListComponent', () => { })) }); - const environmentUseThumbs = { + const environmentUseThumbs = Object.assign({}, environment, { browseBy: { showThumbnails: true } - }; + }); TestBed.configureTestingModule({ imports: [SharedModule, TranslateModule.forRoot()], diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index b8542f5806f..b27e57f7738 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -493,7 +493,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { ); // this adds thumbnail images when required by configuration - let linksToFollow: FollowLinkConfig[] = itemLinksToFollow(this.fetchThumbnail); + let linksToFollow: FollowLinkConfig[] = itemLinksToFollow(this.fetchThumbnail, this.appConfig.item.showAccessStatuses); this.subs.push( observableCombineLatest([ diff --git a/src/app/item-page/item.resolver.ts b/src/app/item-page/item.resolver.ts index ca6a6c5958c..ffeef57ecb0 100644 --- a/src/app/item-page/item.resolver.ts +++ b/src/app/item-page/item.resolver.ts @@ -8,20 +8,27 @@ import { followLink, FollowLinkConfig } from '../shared/utils/follow-link-config import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { Store } from '@ngrx/store'; import { ResolvedAction } from '../core/resolving/resolver.actions'; +import { environment } from '../../environments/environment'; /** * The self links defined in this list are expected to be requested somewhere in the near future * Requesting them as embeds will limit the number of requests */ -export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ - followLink('owningCollection', {}, - followLink('parentCommunity', {}, - followLink('parentCommunity')) - ), - followLink('relationships'), - followLink('version', {}, followLink('versionhistory')), - followLink('thumbnail') -]; +export function getItemPageLinksToFollow(): FollowLinkConfig[] { + const followLinks: FollowLinkConfig[] = [ + followLink('owningCollection', {}, + followLink('parentCommunity', {}, + followLink('parentCommunity')) + ), + followLink('relationships'), + followLink('version', {}, followLink('versionhistory')), + followLink('thumbnail'), + ]; + if (environment.item.showAccessStatuses) { + followLinks.push(followLink('accessStatus')); + } + return followLinks; +} /** * This class represents a resolver that requests a specific item before the route is activated @@ -46,7 +53,7 @@ export class ItemResolver implements Resolve> { const itemRD$ = this.itemService.findById(route.params.id, true, false, - ...ITEM_PAGE_LINKS_TO_FOLLOW + ...getItemPageLinksToFollow(), ).pipe( getFirstCompletedRemoteData(), ); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index ff5a119b6fc..a04c10bf3a5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -328,7 +328,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo const relationship$ = this.relationshipService.findById(this.value.virtualValue, true, true, - ... itemLinksToFollow(this.fetchThumbnail)).pipe( + ... itemLinksToFollow(this.fetchThumbnail, this.appConfig.item.showAccessStatuses)).pipe( getAllSucceededRemoteData(), getRemoteDataPayload()); this.relationshipValue$ = observableCombineLatest([this.item$.pipe(take(1)), relationship$]).pipe( diff --git a/src/app/shared/utils/relation-query.utils.ts b/src/app/shared/utils/relation-query.utils.ts index 62a69075fcb..635ae946da3 100644 --- a/src/app/shared/utils/relation-query.utils.ts +++ b/src/app/shared/utils/relation-query.utils.ts @@ -1,5 +1,6 @@ import { followLink, FollowLinkConfig } from './follow-link-config.model'; import { Relationship } from '../../core/shared/item-relationships/relationship.model'; +import { Item } from '../../core/shared/item.model'; /** * Get the query for looking up items by relation type @@ -21,19 +22,22 @@ export function getFilterByRelation(relationType: string, itemUUID: string): str } /** - * Creates links to follow for the leftItem and rightItem. Links will include - * @param showThumbnail thumbnail image configuration - * @returns followLink array + * Creates links to follow for the leftItem and rightItem. Optionally additional links for `thumbnail` & `accessStatus` + * can be embedded as well. + * + * @param showThumbnail Whether the `thumbnail` needs to be embedded on the {@link Item} + * @param showAccessStatus Whether the `accessStatus` needs to be embedded on the {@link Item} */ -export function itemLinksToFollow(showThumbnail: boolean): FollowLinkConfig[] { - let linksToFollow: FollowLinkConfig[]; +export function itemLinksToFollow(showThumbnail: boolean, showAccessStatus: boolean): FollowLinkConfig[] { + const conditionalLinksToFollow: FollowLinkConfig[] = []; if (showThumbnail) { - linksToFollow = [ - followLink('leftItem',{}, followLink('thumbnail')), - followLink('rightItem',{}, followLink('thumbnail')) - ]; - } else { - linksToFollow = [followLink('leftItem'), followLink('rightItem')]; + conditionalLinksToFollow.push(followLink('thumbnail')); } - return linksToFollow; + if (showAccessStatus) { + conditionalLinksToFollow.push(followLink('accessStatus')); + } + return [ + followLink('leftItem', undefined, ...conditionalLinksToFollow), + followLink('rightItem', undefined, ...conditionalLinksToFollow), + ]; }