diff --git a/config/config.example.yml b/config/config.example.yml index 2cb8f9541be..21629f66d20 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -431,6 +431,8 @@ item: pageSize: 5 # Show the bitstream access status label on the item page showAccessStatuses: false + # Open bitstream download links in a new browser tab by default + openDownloadLinksInNewTab: false # Configuration of metadata to be displayed in the item metadata link view popover metadataLinkViewPopoverData: # Metdadata list to be displayed for entities without a specific configuration diff --git a/src/app/shared/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.html b/src/app/shared/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.html index 366996ee1f0..8eb3c85f732 100644 --- a/src/app/shared/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.html +++ b/src/app/shared/bitstream-attachment/attachment-render/types/file-download-button/file-download-button.component.html @@ -1,18 +1,21 @@ @if (!hasNoDownload) { @if (bitstreamPath$ | async; as bitstreamLink) { @if (canDownload$ | async) { - + } @else { - + } } } diff --git a/src/app/shared/file-download-link/file-download-link.component.spec.ts b/src/app/shared/file-download-link/file-download-link.component.spec.ts index 453ac868a4c..32232c30bd1 100644 --- a/src/app/shared/file-download-link/file-download-link.component.spec.ts +++ b/src/app/shared/file-download-link/file-download-link.component.spec.ts @@ -43,6 +43,19 @@ describe('FileDownloadLinkComponent', () => { let item: Item; let storeMock: any; + const mockAppConfig = { + cache: { + msToLive: { + default: 15 * 60 * 1000, + }, + }, + item: { + bitstream: { + openDownloadLinksInNewTab: true, + }, + }, + }; + const itemRequestStub = Object.assign(new ItemRequest(), { token: 'item-request-token', requestName: 'requester name', @@ -88,7 +101,7 @@ describe('FileDownloadLinkComponent', () => { { provide: ActivatedRoute, useValue: activatedRoute }, { provide: Store, useValue: storeMock }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, - { provide: APP_CONFIG, useValue: { cache: { msToLive: { default: 15 * 60 * 1000 } } } }, + { provide: APP_CONFIG, useValue: mockAppConfig }, ], }) .overrideComponent(FileDownloadLinkComponent, { @@ -129,9 +142,19 @@ describe('FileDownloadLinkComponent', () => { fixture.detectChanges(); const link = fixture.debugElement.query(By.css('a')); expect(link.injector.get(RouterLinkDirectiveStub).routerLink).toContain(new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString()); + expect(link.nativeElement.getAttribute('target')).toBe('_blank'); const lock = fixture.debugElement.query(By.css('.fa-lock')); expect(lock).toBeNull(); }); + + it('should keep an explicit isBlank input over the config default', () => { + component.isBlank = false; + component.ngOnInit(); + scheduler.flush(); + fixture.detectChanges(); + const link = fixture.debugElement.query(By.css('a')); + expect(link.nativeElement.getAttribute('target')).toBe('_self'); + }); }); describe('when the user has no download rights but has the right to request a copy', () => { diff --git a/src/app/shared/file-download-link/file-download-link.component.ts b/src/app/shared/file-download-link/file-download-link.component.ts index f6ddbcf230e..a3de3103e44 100644 --- a/src/app/shared/file-download-link/file-download-link.component.ts +++ b/src/app/shared/file-download-link/file-download-link.component.ts @@ -5,13 +5,19 @@ import { } from '@angular/common'; import { Component, + Inject, Input, OnInit, + Optional, } from '@angular/core'; import { ActivatedRoute, RouterLink, } from '@angular/router'; +import { + APP_CONFIG, + AppConfig, +} from '@dspace/config/app-config.interface'; import { DSONameService } from '@dspace/core/breadcrumbs/dso-name.service'; import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; @@ -78,7 +84,7 @@ export class FileDownloadLinkComponent implements OnInit { /** * A boolean representing if link is shown in same tab or in a new one. */ - @Input() isBlank = false; + @Input() isBlank: boolean; @Input() enableRequestACopy = true; @@ -108,10 +114,13 @@ export class FileDownloadLinkComponent implements OnInit { public dsoNameService: DSONameService, private route: ActivatedRoute, private translateService: TranslateService, + @Optional() @Inject(APP_CONFIG) private appConfig?: AppConfig, ) { } ngOnInit() { + this.isBlank = this.isBlank ?? this.appConfig?.item?.bitstream?.openDownloadLinksInNewTab; + if (this.enableRequestACopy) { // Obtain item request data from the route snapshot this.itemRequest = this.route.snapshot.data.itemRequest; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index fd703d4d024..1801bc2471b 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -412,6 +412,8 @@ export class DefaultAppConfig implements AppConfig { pageSize: 5, // Show the bitstream access status label showAccessStatuses: false, + // Open bitstream download links in a new browser tab by default + openDownloadLinksInNewTab: false, }, // Configuration for the metadata link view popover metadataLinkViewPopoverData: { diff --git a/src/config/item-config.interface.ts b/src/config/item-config.interface.ts index 8c6792ff620..228df8d0395 100644 --- a/src/config/item-config.interface.ts +++ b/src/config/item-config.interface.ts @@ -18,6 +18,8 @@ export interface ItemConfig extends Config { pageSize: number; // Show the bitstream access status label showAccessStatuses: boolean; + // Open bitstream download links in a new browser tab by default + openDownloadLinksInNewTab?: boolean; } metadataLinkViewPopoverData: MetadataLinkViewPopoverDataConfig