From fc4ddd346ffdaf3bfd92f96338b388a53b6ab0b1 Mon Sep 17 00:00:00 2001 From: Andrea Barbasso Date: Wed, 8 Apr 2026 15:18:39 +0200 Subject: [PATCH 1/4] [DURACOM-483] add configuration to open bitstreams in new tabs --- config/config.example.yml | 2 ++ .../file-download-button.component.html | 11 +++++--- .../file-download-link.component.spec.ts | 25 ++++++++++++++++++- .../file-download-link.component.ts | 12 +++++++-- src/config/default-app-config.ts | 2 ++ src/config/item-config.interface.ts | 2 ++ 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 2cb8f9541be..0d02e65a257 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: true # 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..62768d0f73a 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..5471342883f 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,8 +5,10 @@ import { } from '@angular/common'; import { Component, + Inject, Input, OnInit, + Optional, } from '@angular/core'; import { ActivatedRoute, @@ -40,7 +42,10 @@ import { map, switchMap, } from 'rxjs/operators'; - +import { + APP_CONFIG, + AppConfig, +} from '@dspace/config/app-config.interface'; import { ThemedAccessStatusBadgeComponent } from '../object-collection/shared/badges/access-status-badge/themed-access-status-badge.component'; @Component({ @@ -78,7 +83,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 +113,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 ?? true; + 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..fbba1eab79b 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: true, }, // 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 From 65c8c64c3b72732c9c2db8ae836868058de54c93 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 13 May 2026 09:38:44 +0200 Subject: [PATCH 2/4] [DURACOM-483] set config default as false --- config/config.example.yml | 2 +- src/config/default-app-config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 0d02e65a257..21629f66d20 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -432,7 +432,7 @@ item: # Show the bitstream access status label on the item page showAccessStatuses: false # Open bitstream download links in a new browser tab by default - openDownloadLinksInNewTab: true + 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/config/default-app-config.ts b/src/config/default-app-config.ts index fbba1eab79b..1801bc2471b 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -413,7 +413,7 @@ export class DefaultAppConfig implements AppConfig { // Show the bitstream access status label showAccessStatuses: false, // Open bitstream download links in a new browser tab by default - openDownloadLinksInNewTab: true, + openDownloadLinksInNewTab: false, }, // Configuration for the metadata link view popover metadataLinkViewPopoverData: { From e5557d5cdbb38d541f1c8d652916883bce0339b6 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 13 May 2026 09:50:32 +0200 Subject: [PATCH 3/4] [DURACOM-483] fix lint --- .../file-download-link.component.spec.ts | 2 +- .../file-download-link/file-download-link.component.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) 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 62768d0f73a..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 @@ -46,7 +46,7 @@ describe('FileDownloadLinkComponent', () => { const mockAppConfig = { cache: { msToLive: { - default: 15 * 60 * 1000 + default: 15 * 60 * 1000, }, }, item: { 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 5471342883f..3b096921b47 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 @@ -14,6 +14,10 @@ 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'; @@ -42,10 +46,7 @@ import { map, switchMap, } from 'rxjs/operators'; -import { - APP_CONFIG, - AppConfig, -} from '@dspace/config/app-config.interface'; + import { ThemedAccessStatusBadgeComponent } from '../object-collection/shared/badges/access-status-badge/themed-access-status-badge.component'; @Component({ From 7878213309fd8d754d6a58c6199510da0649f937 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 13 May 2026 10:14:01 +0200 Subject: [PATCH 4/4] [DURACOM-483] remove secondary fallback --- .../shared/file-download-link/file-download-link.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3b096921b47..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 @@ -119,7 +119,7 @@ export class FileDownloadLinkComponent implements OnInit { } ngOnInit() { - this.isBlank = this.isBlank ?? this.appConfig?.item?.bitstream?.openDownloadLinksInNewTab ?? true; + this.isBlank = this.isBlank ?? this.appConfig?.item?.bitstream?.openDownloadLinksInNewTab; if (this.enableRequestACopy) { // Obtain item request data from the route snapshot