From dde57b93871c544a903721e526d3d3e430cb2cb7 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Mon, 31 May 2021 16:19:44 +0200 Subject: [PATCH 01/80] update citation_pdf_url logic --- src/app/core/metadata/metadata.service.ts | 133 ++++++++++++++++++---- 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 807f7a42ab..268d361567 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -5,10 +5,10 @@ import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { catchError, distinctUntilKeyChanged, filter, first, map, take } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable, of as observableOf, EMPTY } from 'rxjs'; +import { distinctUntilKeyChanged, filter, map, take, switchMap, expand } from 'rxjs/operators'; -import { hasValue, isNotEmpty } from '../../shared/empty.util'; +import { hasValue, hasNoValue } from '../../shared/empty.util'; import { DSONameService } from '../breadcrumbs/dso-name.service'; import { CacheableObject } from '../cache/object-cache.reducer'; import { BitstreamDataService } from '../data/bitstream-data.service'; @@ -21,11 +21,15 @@ import { DSpaceObject } from '../shared/dspace-object.model'; import { Item } from '../shared/item.model'; import { getFirstSucceededRemoteDataPayload, - getFirstSucceededRemoteListPayload + getFirstCompletedRemoteData } from '../shared/operators'; import { environment } from '../../../environments/environment'; import { RootDataService } from '../data/root-data.service'; import { getBitstreamDownloadRoute } from '../../app-routing-paths'; +import { BundleDataService } from '../data/bundle-data.service'; +import { followLink } from '../../shared/utils/follow-link-config.model'; +import { Bundle } from '../shared/bundle.model'; +import { PaginatedList } from '../data/paginated-list.model'; @Injectable() export class MetadataService { @@ -42,6 +46,7 @@ export class MetadataService { private meta: Meta, private title: Title, private dsoNameService: DSONameService, + private bundleDataService: BundleDataService, private bitstreamDataService: BitstreamDataService, private bitstreamFormatDataService: BitstreamFormatDataService, private rootService: RootDataService @@ -275,26 +280,110 @@ export class MetadataService { private setCitationPdfUrlTag(): void { if (this.currentObject.value instanceof Item) { const item = this.currentObject.value as Item; - this.bitstreamDataService.findAllByItemAndBundleName(item, 'ORIGINAL') - .pipe( - getFirstSucceededRemoteListPayload(), - first((files) => isNotEmpty(files)), - catchError((error) => { - console.debug(error.message); - return []; - })) - .subscribe((bitstreams: Bitstream[]) => { - for (const bitstream of bitstreams) { - this.bitstreamFormatDataService.findByBitstream(bitstream).pipe( - getFirstSucceededRemoteDataPayload() - ).subscribe((format: BitstreamFormat) => { - if (format.mimetype === 'application/pdf') { - const bitstreamLink = getBitstreamDownloadRoute(bitstream); - this.addMetaTag('citation_pdf_url', bitstreamLink); + + // Retrieve the ORIGINAL bundle for the item + this.bundleDataService.findByItemAndName( + item, + 'ORIGINAL', + true, + true, + followLink('primaryBitstream'), + followLink('bitstreams', + undefined, + true, + true, + true, + followLink('format') + ) + ).pipe( + getFirstSucceededRemoteDataPayload(), + switchMap((bundle: Bundle) => + + // First try the primary bitstream + bundle.primaryBitstream.pipe( + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + if (hasValue(rd.payload)) { + return rd.payload; + } else { + return null; } - }); + }), + // return the bundle as well so we can use it again if there's no primary bitstream + map((bitstream: Bitstream) => [bundle, bitstream]) + ) + ), + switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => { + if (hasValue(primaryBitstream)) { + // If there was a primary bitstream, emit its link + return [getBitstreamDownloadRoute(primaryBitstream)]; + } else { + // Otherwise consider the regular bitstreams in the bundle + return bundle.bitstreams.pipe( + getFirstCompletedRemoteData(), + switchMap((bitstreamRd: RemoteData>) => { + if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) { + // If there's only one bitstream in the bundle, emit its link + return [getBitstreamDownloadRoute(bitstreamRd.payload.page[0])]; + } else { + // Otherwise check all bitstreams to see if one matches the format whitelist + return observableOf(bitstreamRd.payload).pipe( + // Because there can be more than one page of bitstreams, this expand operator + // will retrieve them in turn due to the take(1) at the bottom, it will only + // retrieve pages until a match is found + expand((paginatedList: PaginatedList) => { + if (hasNoValue(paginatedList.next)) { + // If there's no next page, stop. + return EMPTY; + } else { + // Otherwise retrieve the next page + return this.bitstreamDataService.findAllByHref( + paginatedList.next, + undefined, + true, + true, + followLink('format') + ).pipe( + getFirstCompletedRemoteData(), + map((next: RemoteData>) => { + if (hasValue(next.payload)) { + return next.payload; + } else { + return EMPTY; + } + }) + ); + } + }), + // Return the array of bitstreams inside each paginated list + map((paginatedList: PaginatedList) => paginatedList.page), + // Emit the bitstreams in the list one at a time + switchMap((bitstreams: Bitstream[]) => bitstreams), + // Retrieve the format for each bitstream + switchMap((bitstream: Bitstream) => bitstream.format.pipe( + getFirstSucceededRemoteDataPayload(), + // Keep the original bitstream, because it, not the format, is what we'll need + // for the link at the end + map((format: BitstreamFormat) => [bitstream, format]) + )), + // Filter out only pairs with whitelisted formats + filter(([, format]: [Bitstream, BitstreamFormat]) => + hasValue(format) && format.mimetype === 'application/pdf'), // TODO change to check map of mimetypes + // We only need 1 + take(1), + // Emit the link of the match + map(([bitstream, ]: [Bitstream, BitstreamFormat]) => getBitstreamDownloadRoute(bitstream)) + ); + } + }) + ); } - }); + }), + take(1) + ).subscribe((link: string) => { + // Use the found link to set the tag + this.addMetaTag('citation_pdf_url', link); + }); } } From 9fe5a91bc2894b7c6fb04e28190323c4efc34d19 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Thu, 3 Jun 2021 16:30:57 +0200 Subject: [PATCH 02/80] 79768: Extract download route method for Bundles with multiple Bitstreams --- src/app/core/metadata/metadata.service.ts | 98 ++++++++++++----------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 268d361567..fa4e81a5d7 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -327,53 +327,7 @@ export class MetadataService { return [getBitstreamDownloadRoute(bitstreamRd.payload.page[0])]; } else { // Otherwise check all bitstreams to see if one matches the format whitelist - return observableOf(bitstreamRd.payload).pipe( - // Because there can be more than one page of bitstreams, this expand operator - // will retrieve them in turn due to the take(1) at the bottom, it will only - // retrieve pages until a match is found - expand((paginatedList: PaginatedList) => { - if (hasNoValue(paginatedList.next)) { - // If there's no next page, stop. - return EMPTY; - } else { - // Otherwise retrieve the next page - return this.bitstreamDataService.findAllByHref( - paginatedList.next, - undefined, - true, - true, - followLink('format') - ).pipe( - getFirstCompletedRemoteData(), - map((next: RemoteData>) => { - if (hasValue(next.payload)) { - return next.payload; - } else { - return EMPTY; - } - }) - ); - } - }), - // Return the array of bitstreams inside each paginated list - map((paginatedList: PaginatedList) => paginatedList.page), - // Emit the bitstreams in the list one at a time - switchMap((bitstreams: Bitstream[]) => bitstreams), - // Retrieve the format for each bitstream - switchMap((bitstream: Bitstream) => bitstream.format.pipe( - getFirstSucceededRemoteDataPayload(), - // Keep the original bitstream, because it, not the format, is what we'll need - // for the link at the end - map((format: BitstreamFormat) => [bitstream, format]) - )), - // Filter out only pairs with whitelisted formats - filter(([, format]: [Bitstream, BitstreamFormat]) => - hasValue(format) && format.mimetype === 'application/pdf'), // TODO change to check map of mimetypes - // We only need 1 - take(1), - // Emit the link of the match - map(([bitstream, ]: [Bitstream, BitstreamFormat]) => getBitstreamDownloadRoute(bitstream)) - ); + return this.getBitstreamDownloadRoute(bitstreamRd); } }) ); @@ -387,6 +341,56 @@ export class MetadataService { } } + private getBitstreamDownloadRoute(bitstreamRd: RemoteData>): Observable { + return observableOf(bitstreamRd.payload).pipe( + // Because there can be more than one page of bitstreams, this expand operator + // will retrieve them in turn due to the take(1) at the bottom, it will only + // retrieve pages until a match is found + expand((paginatedList: PaginatedList) => { + if (hasNoValue(paginatedList.next)) { + // If there's no next page, stop. + return EMPTY; + } else { + // Otherwise retrieve the next page + return this.bitstreamDataService.findAllByHref( + paginatedList.next, + undefined, + true, + true, + followLink('format') + ).pipe( + getFirstCompletedRemoteData(), + map((next: RemoteData>) => { + if (hasValue(next.payload)) { + return next.payload; + } else { + return EMPTY; + } + }) + ); + } + }), + // Return the array of bitstreams inside each paginated list + map((paginatedList: PaginatedList) => paginatedList.page), + // Emit the bitstreams in the list one at a time + switchMap((bitstreams: Bitstream[]) => bitstreams), + // Retrieve the format for each bitstream + switchMap((bitstream: Bitstream) => bitstream.format.pipe( + getFirstSucceededRemoteDataPayload(), + // Keep the original bitstream, because it, not the format, is what we'll need + // for the link at the end + map((format: BitstreamFormat) => [bitstream, format]) + )), + // Filter out only pairs with whitelisted formats + filter(([, format]: [Bitstream, BitstreamFormat]) => + hasValue(format) && format.mimetype === 'application/pdf'), // TODO change to check map of mimetypes + // We only need 1 + take(1), + // Emit the link of the match + map(([bitstream, ]: [Bitstream, BitstreamFormat]) => getBitstreamDownloadRoute(bitstream)) + ); + } + /** * Add to the containing the current DSpace version */ From 1caba78b4df6b9fcec8a99c8d10010c0596da0d8 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Thu, 3 Jun 2021 16:33:58 +0200 Subject: [PATCH 03/80] 79768: Check against list of allowed mimetypes --- src/app/core/metadata/metadata.service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index fa4e81a5d7..0b63e13976 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -40,6 +40,15 @@ export class MetadataService { private currentObject: BehaviorSubject; + private readonly ALLOWED_MIMETYPES = [ + 'application/pdf', // .pdf + 'application/postscript', // .ps + 'application/msword', // .doc + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document ', // .docx + 'application/rtf', // .rtf + 'application/epub+zip', // .epub + ]; + constructor( private router: Router, private translate: TranslateService, @@ -383,7 +392,7 @@ export class MetadataService { )), // Filter out only pairs with whitelisted formats filter(([, format]: [Bitstream, BitstreamFormat]) => - hasValue(format) && format.mimetype === 'application/pdf'), // TODO change to check map of mimetypes + hasValue(format) && this.ALLOWED_MIMETYPES.includes(format.mimetype)), // We only need 1 take(1), // Emit the link of the match From cb4446b79d17e7acc20b82f4855ef94e39307592 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 4 Jun 2021 13:57:30 +0200 Subject: [PATCH 04/80] 79768: Update & add unit tests --- .../core/metadata/metadata.service.spec.ts | 83 +++++++++++-- src/app/core/metadata/metadata.service.ts | 2 +- src/app/shared/mocks/item.mock.ts | 117 ++++++++++++------ 3 files changed, 154 insertions(+), 48 deletions(-) diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index 18421dd489..0a4377e1b7 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -15,11 +15,9 @@ import { RemoteData } from '../data/remote-data'; import { Item } from '../shared/item.model'; import { - ItemMock, - MockBitstream1, - MockBitstream2, - MockBitstreamFormat1, - MockBitstreamFormat2 + ItemMock, MockBitstream1, MockBitstream2, MockBitstream3, MockBitstreamFormat1, MockBitstreamFormat2, + MockBitstreamFormat3, + MockOriginalBundle, } from '../../shared/mocks/item.mock'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -51,10 +49,11 @@ import { UUIDService } from '../shared/uuid.service'; import { MetadataService } from './metadata.service'; import { environment } from '../../../environments/environment'; import { storeModuleConfig } from '../../app.reducer'; -import { HardRedirectService } from '../services/hard-redirect.service'; -import { URLCombiner } from '../url-combiner/url-combiner'; import { RootDataService } from '../data/root-data.service'; import { Root } from '../data/root.model'; +import { Bundle } from '../shared/bundle.model'; +import { BundleDataService } from '../data/bundle-data.service'; +import { createPaginatedList } from '../../shared/testing/utils.test'; /* tslint:disable:max-classes-per-file */ @Component({ @@ -92,6 +91,8 @@ describe('MetadataService', () => { let uuidService: UUIDService; let remoteDataBuildService: RemoteDataBuildService; let itemDataService: ItemDataService; + let bundleDataService; + let bitstreamDataService; let authService: AuthService; let rootService: RootDataService; let translateService: TranslateService; @@ -111,7 +112,7 @@ describe('MetadataService', () => { uuidService = new UUIDService(); requestService = new RequestService(objectCacheService, uuidService, store, undefined); remoteDataBuildService = new RemoteDataBuildService(objectCacheService, undefined, requestService); - const mockBitstreamDataService = { + bitstreamDataService = { findAllByItemAndBundleName(item: Item, bundleName: string, options?: FindListOptions, ...linksToFollow: FollowLinkConfig[]): Observable>> { if (item.equals(ItemMock)) { return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [MockBitstream1, MockBitstream2])); @@ -119,6 +120,7 @@ describe('MetadataService', () => { return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); } }, + findAllByHref: jasmine.createSpy(), }; const mockBitstreamFormatDataService = { findByBitstream(bitstream: Bitstream): Observable> { @@ -129,11 +131,17 @@ describe('MetadataService', () => { case MockBitstream2: return createSuccessfulRemoteDataObject$(MockBitstreamFormat2); break; + case MockBitstream3: + return createSuccessfulRemoteDataObject$(MockBitstreamFormat3); + break; default: return createSuccessfulRemoteDataObject$(new BitstreamFormat()); } } }; + bundleDataService = jasmine.createSpyObj('bundleDataService', { + findByItemAndName: createSuccessfulRemoteDataObject$(MockOriginalBundle), + }); rootService = jasmine.createSpyObj('rootService', { findRoot: createSuccessfulRemoteDataObject$(Object.assign(new Root(), { dspaceVersion: 'mock-dspace-version' @@ -176,7 +184,8 @@ describe('MetadataService', () => { { provide: CommunityDataService, useValue: {} }, { provide: DefaultChangeAnalyzer, useValue: {} }, { provide: BitstreamFormatDataService, useValue: mockBitstreamFormatDataService }, - { provide: BitstreamDataService, useValue: mockBitstreamDataService }, + { provide: BitstreamDataService, useValue: bitstreamDataService }, + { provide: BundleDataService, useValue: bundleDataService }, { provide: RootDataService, useValue: rootService }, Meta, Title, @@ -264,8 +273,42 @@ describe('MetadataService', () => { }); + describe('citation_pdf_url', () => { + it('should link to primary Bitstream URL regardless of format', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock)); + bundleDataService.findByItemAndName.and.returnValue(mockBundleRD$([], MockBitstream3)); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/download'); + })); + + describe('no primary Bitstream', () => { + it('should link to first and only Bitstream regardless of format', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock)); + bundleDataService.findByItemAndName.and.returnValue(mockBundleRD$([MockBitstream3])); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/download'); + })); + + it('should link to first Bitstream with allowed format', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock)); + + const bitstreams = [MockBitstream3, MockBitstream3, MockBitstream1]; + bundleDataService.findByItemAndName.and.returnValue(mockBundleRD$(bitstreams)); + bitstreamDataService.findAllByHref.and.returnValues( + ...mockBitstreamPages$(bitstreams).map(bp => createSuccessfulRemoteDataObject$(bp)), + ); + + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/download'); + })); + }); + }); + const mockRemoteData = (mockItem: Item): Observable> => { - return createSuccessfulRemoteDataObject$(ItemMock); + return createSuccessfulRemoteDataObject$(mockItem); }; const mockType = (mockItem: Item, type: string): Item => { @@ -285,4 +328,24 @@ describe('MetadataService', () => { return publishedMockItem; }; + const mockBundleRD$ = (bitstreams: Bitstream[], primary?: Bitstream): Observable> => { + return createSuccessfulRemoteDataObject$( + Object.assign(new Bundle(), { + name: 'ORIGINAL', + bitstreams: createSuccessfulRemoteDataObject$(mockBitstreamPages$(bitstreams)[0]), + primaryBitstream: createSuccessfulRemoteDataObject$(primary), + }) + ); + }; + + const mockBitstreamPages$ = (bitstreams: Bitstream[]): PaginatedList[] => { + return bitstreams.map((bitstream, index) => Object.assign(createPaginatedList([bitstream]), { + pageInfo: { + totalElements: bitstreams.length, // announce multiple elements/pages + }, + _links: index < bitstreams.length - 1 + ? { next: { href: 'not empty' }} // fake link to the next bitstream page + : { next: { href: undefined }}, // last page has no link + })); + }; }); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 0b63e13976..848b48b8c5 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -44,7 +44,7 @@ export class MetadataService { 'application/pdf', // .pdf 'application/postscript', // .ps 'application/msword', // .doc - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document ', // .docx + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx 'application/rtf', // .rtf 'application/epub+zip', // .epub ]; diff --git a/src/app/shared/mocks/item.mock.ts b/src/app/shared/mocks/item.mock.ts index 945b0f7816..10eab2da00 100644 --- a/src/app/shared/mocks/item.mock.ts +++ b/src/app/shared/mocks/item.mock.ts @@ -5,6 +5,7 @@ import { Bitstream } from '../../core/shared/bitstream.model'; import { Item } from '../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { createPaginatedList } from '../testing/utils.test'; +import { Bundle } from '../../core/shared/bundle.model'; export const MockBitstreamFormat1: BitstreamFormat = Object.assign(new BitstreamFormat(), { shortDescription: 'Microsoft Word XML', @@ -34,11 +35,25 @@ export const MockBitstreamFormat2: BitstreamFormat = Object.assign(new Bitstream } }); +export const MockBitstreamFormat3: BitstreamFormat = Object.assign(new BitstreamFormat(), { + shortDescription: 'Binary', + description: 'Some scary unknown binary file', + mimetype: 'application/octet-stream', + supportLevel: 0, + internal: false, + extensions: null, + _links:{ + self: { + href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreamformats/17' + } + } +}); + export const MockBitstream1: Bitstream = Object.assign(new Bitstream(), { sizeBytes: 10201, content: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content', - format: observableOf(MockBitstreamFormat1), + format: createSuccessfulRemoteDataObject$(MockBitstreamFormat1), bundleName: 'ORIGINAL', _links:{ self: { @@ -61,7 +76,7 @@ export const MockBitstream1: Bitstream = Object.assign(new Bitstream(), export const MockBitstream2: Bitstream = Object.assign(new Bitstream(), { sizeBytes: 31302, content: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/99b00f3c-1cc6-4689-8158-91965bee6b28/content', - format: observableOf(MockBitstreamFormat2), + format: createSuccessfulRemoteDataObject$(MockBitstreamFormat2), bundleName: 'ORIGINAL', id: '99b00f3c-1cc6-4689-8158-91965bee6b28', uuid: '99b00f3c-1cc6-4689-8158-91965bee6b28', @@ -82,6 +97,68 @@ export const MockBitstream2: Bitstream = Object.assign(new Bitstream(), { } }); +export const MockBitstream3: Bitstream = Object.assign(new Bitstream(), { + sizeBytes: 4975123, + content: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/content', + format: createSuccessfulRemoteDataObject$(MockBitstreamFormat3), + bundleName: 'ORIGINAL', + id: '4db100c1-e1f5-4055-9404-9bc3e2d15f29', + uuid: '4db100c1-e1f5-4055-9404-9bc3e2d15f29', + type: 'bitstream', + _links: { + self: { href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29' }, + content: { href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/content' }, + format: { href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreamformats/17' }, + bundle: { href: '' } + }, + metadata: { + 'dc.title': [ + { + language: null, + value: 'scary' + } + ] + } +}); + +export const MockOriginalBundle: Bundle = Object.assign(new Bundle(), { + name: 'ORIGINAL', + primaryBitstream: createSuccessfulRemoteDataObject$(MockBitstream2), + bitstreams: observableOf(Object.assign({ + _links: { + self: { + href: 'dspace-angular://aggregated/object/1507836003548', + } + }, + requestPending: false, + responsePending: false, + isSuccessful: true, + errorMessage: '', + state: '', + error: undefined, + isRequestPending: false, + isResponsePending: false, + isLoading: false, + hasFailed: false, + hasSucceeded: true, + statusCode: '202', + pageInfo: {}, + payload: { + pageInfo: { + elementsPerPage: 20, + totalElements: 3, + totalPages: 1, + currentPage: 2 + }, + page: [ + MockBitstream1, + MockBitstream2 + ] + } + })) +}); + + /* tslint:disable:no-shadowed-variable */ export const ItemMock: Item = Object.assign(new Item(), { handle: '10673/6', @@ -90,41 +167,7 @@ export const ItemMock: Item = Object.assign(new Item(), { isDiscoverable: true, isWithdrawn: false, bundles: createSuccessfulRemoteDataObject$(createPaginatedList([ - { - name: 'ORIGINAL', - bitstreams: observableOf(Object.assign({ - _links: { - self: { - href: 'dspace-angular://aggregated/object/1507836003548', - } - }, - requestPending: false, - responsePending: false, - isSuccessful: true, - errorMessage: '', - state: '', - error: undefined, - isRequestPending: false, - isResponsePending: false, - isLoading: false, - hasFailed: false, - hasSucceeded: true, - statusCode: '202', - pageInfo: {}, - payload: { - pageInfo: { - elementsPerPage: 20, - totalElements: 3, - totalPages: 1, - currentPage: 2 - }, - page: [ - MockBitstream1, - MockBitstream2 - ] - } - })) - } + MockOriginalBundle, ])), _links:{ self: { From 6f7b76ec39efa00676065bb50ef23c66804d96ca Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 4 Jun 2021 14:02:44 +0200 Subject: [PATCH 05/80] 79768: Remove og:title & og:description --- src/app/core/metadata/metadata.service.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 848b48b8c5..7caa7fe6cf 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -62,10 +62,6 @@ export class MetadataService { ) { // TODO: determine what open graph meta tags are needed and whether // the differ per route. potentially add image based on DSpaceObject - this.meta.addTags([ - { property: 'og:title', content: 'DSpace Angular Universal' }, - { property: 'og:description', content: 'The modern front-end for DSpace 7.' } - ]); this.initialized = false; this.tagStore = new Map(); } From 67f8ce7849ad222acb8bf171056e2a27cd66f7b8 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 4 Jun 2021 14:02:19 +0200 Subject: [PATCH 06/80] 79768: Rename citation_date to citation_publication_date --- src/app/core/metadata/metadata.service.spec.ts | 2 +- src/app/core/metadata/metadata.service.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index 0a4377e1b7..749074d0a7 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -218,7 +218,7 @@ describe('MetadataService', () => { expect(title.getTitle()).toEqual('Test PowerPoint Document'); expect(tagStore.get('citation_title')[0].content).toEqual('Test PowerPoint Document'); expect(tagStore.get('citation_author')[0].content).toEqual('Doe, Jane'); - expect(tagStore.get('citation_date')[0].content).toEqual('1650-06-26'); + expect(tagStore.get('citation_publication_date')[0].content).toEqual('1650-06-26'); expect(tagStore.get('citation_issn')[0].content).toEqual('123456789'); expect(tagStore.get('citation_language')[0].content).toEqual('en'); expect(tagStore.get('citation_keywords')[0].content).toEqual('keyword1; keyword2; keyword3'); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 7caa7fe6cf..3ccc9e5f09 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -135,7 +135,7 @@ export class MetadataService { this.setCitationTitleTag(); this.setCitationAuthorTags(); - this.setCitationDateTag(); + this.setCitationPublicationDateTag(); this.setCitationISSNTag(); this.setCitationISBNTag(); @@ -208,9 +208,9 @@ export class MetadataService { /** * Add to the */ - private setCitationDateTag(): void { + private setCitationPublicationDateTag(): void { const value = this.getFirstMetaTagValue(['dc.date.copyright', 'dc.date.issued', 'dc.date.available', 'dc.date.accessioned']); - this.addMetaTag('citation_date', value); + this.addMetaTag('citation_publication_date', value); } /** From 304d8f73862fd577d687813bdaad07d6f7d32b3d Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 4 Jun 2021 14:08:20 +0200 Subject: [PATCH 07/80] 79768: Fix tag: citation_abstract_html_url --- .../core/metadata/metadata.service.spec.ts | 23 ++++++++++++++++++- src/app/core/metadata/metadata.service.ts | 7 ++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index 749074d0a7..30ee7f99ea 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -230,7 +230,6 @@ describe('MetadataService', () => { tick(); expect(tagStore.get('citation_dissertation_name')[0].content).toEqual('Test PowerPoint Document'); expect(tagStore.get('citation_dissertation_institution')[0].content).toEqual('Mock Publisher'); - expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual([environment.ui.baseUrl, router.url].join('')); expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/99b00f3c-1cc6-4689-8158-91965bee6b28/download'); })); @@ -273,6 +272,22 @@ describe('MetadataService', () => { }); + describe('citation_abstract_html_url', () => { + it('should use dc.identifier.uri if available', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockUri(ItemMock, 'https://ddg.gg'))); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual('https://ddg.gg'); + })); + + it('should use current route as fallback', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockUri(ItemMock))); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual('/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357'); + })); + }); + describe('citation_pdf_url', () => { it('should link to primary Bitstream URL regardless of format', fakeAsync(() => { spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock)); @@ -328,6 +343,12 @@ describe('MetadataService', () => { return publishedMockItem; }; + const mockUri = (mockItem: Item, uri?: string): Item => { + const publishedMockItem = Object.assign(new Item(), mockItem) as Item; + publishedMockItem.metadata['dc.identifier.uri'] = [{ value: uri }] as MetadataValue[]; + return publishedMockItem; + }; + const mockBundleRD$ = (bitstreams: Bitstream[], primary?: Bitstream): Observable> => { return createSuccessfulRemoteDataObject$( Object.assign(new Bundle(), { diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 3ccc9e5f09..bc26f36fd1 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -274,8 +274,11 @@ export class MetadataService { */ private setCitationAbstractUrlTag(): void { if (this.currentObject.value instanceof Item) { - const value = [environment.ui.baseUrl, this.router.url].join(''); - this.addMetaTag('citation_abstract_html_url', value); + let url = this.getMetaTagValue('dc.identifier.uri'); + if (hasNoValue(url)) { + url = this.router.url; // Google should handle relative URL + } + this.addMetaTag('citation_abstract_html_url', url); } } From 81b76dd32774d68bcf2ce87a5ab9e4deeddd71d7 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 4 Jun 2021 14:42:16 +0200 Subject: [PATCH 08/80] 79768: Fix tag: citation_publisher / citation_*_institution --- .../core/metadata/metadata.service.spec.ts | 32 +++++++++++++++++-- src/app/core/metadata/metadata.service.ts | 26 ++++++--------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index 30ee7f99ea..d2656d6c70 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -229,7 +229,6 @@ describe('MetadataService', () => { router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); expect(tagStore.get('citation_dissertation_name')[0].content).toEqual('Test PowerPoint Document'); - expect(tagStore.get('citation_dissertation_institution')[0].content).toEqual('Mock Publisher'); expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/99b00f3c-1cc6-4689-8158-91965bee6b28/download'); })); @@ -284,7 +283,36 @@ describe('MetadataService', () => { spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockUri(ItemMock))); router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); - expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual('/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357'); + expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual('/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357'); + })); + }); + + describe('citation_*_institution / citation_publisher', () => { + it('should use citation_dissertation_institution tag for dissertations', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockPublisher(mockType(ItemMock, 'Thesis')))); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_dissertation_institution')[0].content).toEqual('Mock Publisher'); + expect(tagStore.get('citation_technical_report_institution')).toBeFalsy(); + expect(tagStore.get('citation_publisher')).toBeFalsy(); + })); + + it('should use citation_tech_report_institution tag for tech reports', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockPublisher(mockType(ItemMock, 'Technical Report')))); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_dissertation_institution')).toBeFalsy(); + expect(tagStore.get('citation_technical_report_institution')[0].content).toEqual('Mock Publisher'); + expect(tagStore.get('citation_publisher')).toBeFalsy(); + })); + + it('should use citation_publisher for other item types', fakeAsync(() => { + spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockPublisher(mockType(ItemMock, 'Some Other Type')))); + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + tick(); + expect(tagStore.get('citation_dissertation_institution')).toBeFalsy(); + expect(tagStore.get('citation_technical_report_institution')).toBeFalsy(); + expect(tagStore.get('citation_publisher')[0].content).toEqual('Mock Publisher'); })); }); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index bc26f36fd1..f4b7b7ca10 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -144,14 +144,10 @@ export class MetadataService { this.setCitationAbstractUrlTag(); this.setCitationPdfUrlTag(); + this.setCitationPublisherTag(); if (this.isDissertation()) { this.setCitationDissertationNameTag(); - this.setCitationDissertationInstitutionTag(); - } - - if (this.isTechReport()) { - this.setCitationTechReportInstitutionTag(); } // this.setCitationJournalTitleTag(); @@ -246,19 +242,17 @@ export class MetadataService { } /** - * Add to the + * Add dc.publisher to the . The tag name depends on the item type. */ - private setCitationDissertationInstitutionTag(): void { + private setCitationPublisherTag(): void { const value = this.getMetaTagValue('dc.publisher'); - this.addMetaTag('citation_dissertation_institution', value); - } - - /** - * Add to the - */ - private setCitationTechReportInstitutionTag(): void { - const value = this.getMetaTagValue('dc.publisher'); - this.addMetaTag('citation_technical_report_institution', value); + if (this.isDissertation()) { + this.addMetaTag('citation_dissertation_institution', value); + } else if (this.isTechReport()) { + this.addMetaTag('citation_technical_report_institution', value); + } else { + this.addMetaTag('citation_publisher', value); + } } /** From 6a4e56322f70768b52301adaaf5f903fa5572937 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Fri, 4 Jun 2021 15:14:57 +0200 Subject: [PATCH 09/80] 79768: Rename method --- src/app/core/metadata/metadata.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index f4b7b7ca10..7b886d9067 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -329,7 +329,7 @@ export class MetadataService { return [getBitstreamDownloadRoute(bitstreamRd.payload.page[0])]; } else { // Otherwise check all bitstreams to see if one matches the format whitelist - return this.getBitstreamDownloadRoute(bitstreamRd); + return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); } }) ); @@ -343,7 +343,7 @@ export class MetadataService { } } - private getBitstreamDownloadRoute(bitstreamRd: RemoteData>): Observable { + private getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { return observableOf(bitstreamRd.payload).pipe( // Because there can be more than one page of bitstreams, this expand operator // will retrieve them in turn due to the take(1) at the bottom, it will only From 2ed16aa66ecf81ad05c85d2fb21f65774d554fa9 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Mon, 14 Jun 2021 09:34:23 +0200 Subject: [PATCH 10/80] 79768: Update typedocs --- src/app/core/metadata/metadata.service.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 7b886d9067..fa1022e4f6 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -40,7 +40,13 @@ export class MetadataService { private currentObject: BehaviorSubject; - private readonly ALLOWED_MIMETYPES = [ + /** + * When generating the citation_pdf_url meta tag for Items with more than one Bitstream (and no primary Bitstream), + * the first Bitstream to match one of the following MIME types is selected. + * See {@linkcode getFirstAllowedFormatBitstreamLink} + * @private + */ + private readonly CITATION_PDF_URL_MIMETYPES = [ 'application/pdf', // .pdf 'application/postscript', // .ps 'application/msword', // .doc @@ -202,7 +208,7 @@ export class MetadataService { } /** - * Add to the + * Add to the */ private setCitationPublicationDateTag(): void { const value = this.getFirstMetaTagValue(['dc.date.copyright', 'dc.date.issued', 'dc.date.available', 'dc.date.accessioned']); @@ -343,6 +349,12 @@ export class MetadataService { } } + /** + * For Items with more than one Bitstream (and no primary Bitstream), link to the first Bitstream with a MIME type + * included in {@linkcode CITATION_PDF_URL_MIMETYPES} + * @param bitstreamRd + * @private + */ private getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { return observableOf(bitstreamRd.payload).pipe( // Because there can be more than one page of bitstreams, this expand operator @@ -385,7 +397,7 @@ export class MetadataService { )), // Filter out only pairs with whitelisted formats filter(([, format]: [Bitstream, BitstreamFormat]) => - hasValue(format) && this.ALLOWED_MIMETYPES.includes(format.mimetype)), + hasValue(format) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)), // We only need 1 take(1), // Emit the link of the match From 64049fdcf770b43cff44cb7228b003bc879c2a5b Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Mon, 14 Jun 2021 09:48:33 +0200 Subject: [PATCH 11/80] 79768: Make relative URLs absolute --- src/app/core/metadata/metadata.service.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index fa1022e4f6..cef3a67054 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -30,6 +30,8 @@ import { BundleDataService } from '../data/bundle-data.service'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { Bundle } from '../shared/bundle.model'; import { PaginatedList } from '../data/paginated-list.model'; +import { URLCombiner } from '../url-combiner/url-combiner'; +import { HardRedirectService } from '../services/hard-redirect.service'; @Injectable() export class MetadataService { @@ -64,7 +66,8 @@ export class MetadataService { private bundleDataService: BundleDataService, private bitstreamDataService: BitstreamDataService, private bitstreamFormatDataService: BitstreamFormatDataService, - private rootService: RootDataService + private rootService: RootDataService, + private hardRedirectService: HardRedirectService, ) { // TODO: determine what open graph meta tags are needed and whether // the differ per route. potentially add image based on DSpaceObject @@ -276,7 +279,7 @@ export class MetadataService { if (this.currentObject.value instanceof Item) { let url = this.getMetaTagValue('dc.identifier.uri'); if (hasNoValue(url)) { - url = this.router.url; // Google should handle relative URL + url = new URLCombiner(this.hardRedirectService.getRequestOrigin(), this.router.url).toString(); } this.addMetaTag('citation_abstract_html_url', url); } @@ -344,7 +347,10 @@ export class MetadataService { take(1) ).subscribe((link: string) => { // Use the found link to set the tag - this.addMetaTag('citation_pdf_url', link); + this.addMetaTag( + 'citation_pdf_url', + new URLCombiner(this.hardRedirectService.getRequestOrigin(), link).toString() + ); }); } } From 04b4f1cf58dc2f606f4eb120d66f072e2587bf8b Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Mon, 14 Jun 2021 10:30:17 +0200 Subject: [PATCH 12/80] Fix comment punctuation --- src/app/core/metadata/metadata.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index cef3a67054..4e3da11bf2 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -364,7 +364,7 @@ export class MetadataService { private getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { return observableOf(bitstreamRd.payload).pipe( // Because there can be more than one page of bitstreams, this expand operator - // will retrieve them in turn due to the take(1) at the bottom, it will only + // will retrieve them in turn. Due to the take(1) at the bottom, it will only // retrieve pages until a match is found expand((paginatedList: PaginatedList) => { if (hasNoValue(paginatedList.next)) { From 34b117efe3a4918a81c4d3ee904808f29885fe4d Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Mon, 14 Jun 2021 10:50:59 +0200 Subject: [PATCH 13/80] 80084: Fix unit test & LGTM issues --- src/app/core/metadata/metadata.service.spec.ts | 17 +++++++++++------ src/app/core/metadata/metadata.service.ts | 1 - 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index d2656d6c70..a2f2272219 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -47,13 +47,13 @@ import { PageInfo } from '../shared/page-info.model'; import { UUIDService } from '../shared/uuid.service'; import { MetadataService } from './metadata.service'; -import { environment } from '../../../environments/environment'; import { storeModuleConfig } from '../../app.reducer'; import { RootDataService } from '../data/root-data.service'; import { Root } from '../data/root.model'; import { Bundle } from '../shared/bundle.model'; import { BundleDataService } from '../data/bundle-data.service'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { HardRedirectService } from '../services/hard-redirect.service'; /* tslint:disable:max-classes-per-file */ @Component({ @@ -96,6 +96,7 @@ describe('MetadataService', () => { let authService: AuthService; let rootService: RootDataService; let translateService: TranslateService; + let hardRedirectService: HardRedirectService; let location: Location; let router: Router; @@ -147,6 +148,9 @@ describe('MetadataService', () => { dspaceVersion: 'mock-dspace-version' })) }); + hardRedirectService = jasmine.createSpyObj('hardRedirectService', { + getRequestOrigin: 'https://request.org', + }); TestBed.configureTestingModule({ imports: [ @@ -187,6 +191,7 @@ describe('MetadataService', () => { { provide: BitstreamDataService, useValue: bitstreamDataService }, { provide: BundleDataService, useValue: bundleDataService }, { provide: RootDataService, useValue: rootService }, + { provide: HardRedirectService, useValue: hardRedirectService }, Meta, Title, // tslint:disable-next-line:no-empty @@ -229,7 +234,7 @@ describe('MetadataService', () => { router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); expect(tagStore.get('citation_dissertation_name')[0].content).toEqual('Test PowerPoint Document'); - expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/99b00f3c-1cc6-4689-8158-91965bee6b28/download'); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('https://request.org/bitstreams/99b00f3c-1cc6-4689-8158-91965bee6b28/download'); })); it('items page should set meta tags as published Technical Report', fakeAsync(() => { @@ -283,7 +288,7 @@ describe('MetadataService', () => { spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockUri(ItemMock))); router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); - expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual('/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357'); + expect(tagStore.get('citation_abstract_html_url')[0].content).toEqual('https://request.org/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357'); })); }); @@ -322,7 +327,7 @@ describe('MetadataService', () => { bundleDataService.findByItemAndName.and.returnValue(mockBundleRD$([], MockBitstream3)); router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); - expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/download'); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('https://request.org/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/download'); })); describe('no primary Bitstream', () => { @@ -331,7 +336,7 @@ describe('MetadataService', () => { bundleDataService.findByItemAndName.and.returnValue(mockBundleRD$([MockBitstream3])); router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); - expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/download'); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('https://request.org/bitstreams/4db100c1-e1f5-4055-9404-9bc3e2d15f29/download'); })); it('should link to first Bitstream with allowed format', fakeAsync(() => { @@ -345,7 +350,7 @@ describe('MetadataService', () => { router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); tick(); - expect(tagStore.get('citation_pdf_url')[0].content).toEqual('/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/download'); + expect(tagStore.get('citation_pdf_url')[0].content).toEqual('https://request.org/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/download'); })); }); }); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 4e3da11bf2..d6518b6164 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -23,7 +23,6 @@ import { getFirstSucceededRemoteDataPayload, getFirstCompletedRemoteData } from '../shared/operators'; -import { environment } from '../../../environments/environment'; import { RootDataService } from '../data/root-data.service'; import { getBitstreamDownloadRoute } from '../../app-routing-paths'; import { BundleDataService } from '../data/bundle-data.service'; From ea2030666c0c54dad1e9f7c248f50a2faae50e1b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 24 Jun 2021 10:55:46 +0200 Subject: [PATCH 14/80] fix issue with aria-expanded attribute that is invalid on input --- ...dynamic-scrollable-dropdown.component.html | 29 +++++++++++-------- ...dynamic-scrollable-dropdown.component.scss | 8 +++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html index 8244970ca1..01ae9b9728 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html @@ -1,16 +1,21 @@
- +
+ + +