diff --git a/src/app/core/metadata/metadata-bitstream.model.ts b/src/app/core/metadata/metadata-bitstream.model.ts index c5eed0594a4..5d89c0a2a85 100644 --- a/src/app/core/metadata/metadata-bitstream.model.ts +++ b/src/app/core/metadata/metadata-bitstream.model.ts @@ -80,6 +80,20 @@ export class MetadataBitstream extends ListableObject implements HALResource { @autoserialize canPreview: boolean; + /** + * The access status of this bitstream (e.g. "open.access", "embargo", "restricted"). + * Unlike the standard Bitstream HAL resource, this endpoint embeds the status directly + * rather than exposing it via a resolvable accessStatus link. + */ + @autoserialize + status: string; + + /** + * The date this bitstream's embargo lifts. Only set when status is "embargo". + */ + @autoserialize + embargoDate: string; + /** * The {@link HALLink}s for this MetadataField */ diff --git a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html index 4d4e95b0b49..5e2f5abe13a 100644 --- a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html +++ b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html @@ -8,7 +8,13 @@
{{'item.file.description.name' | translate}}
- {{ fileInput.name }} + + + {{ fileInput.name }} + + {{ 'embargo.listelement.badge' | translate: { date: fileInput.embargoDate } }} + + {{ fileInput.name }}
{{'item.file.description.size' | translate}}
diff --git a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.spec.ts b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.spec.ts index 19cd1bf778f..210dca9999c 100644 --- a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.spec.ts +++ b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.spec.ts @@ -12,6 +12,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HALEndpointService } from '../../../../../core/shared/hal-endpoint.service'; import { FileSizePipe } from '../../../../../shared/utils/file-size-pipe'; +import { environment } from '../../../../../../environments/environment'; describe('FileDescriptionComponent', () => { let component: FileDescriptionComponent; @@ -47,11 +48,7 @@ describe('FileDescriptionComponent', () => { }).compileComponents(); }); - beforeEach(() => { - fixture = TestBed.createComponent(FileDescriptionComponent); - component = fixture.componentInstance; - - // Mock the input value + function createFileInput(overrides: Partial = {}): MetadataBitstream { const fileInput = new MetadataBitstream(); fileInput.id = 123; fileInput.name = 'testFile'; @@ -66,20 +63,86 @@ describe('FileDescriptionComponent', () => { self: { href: '' }, schema: { href: '' }, }; + return Object.assign(fileInput, overrides); + } + + describe('by default', () => { + beforeEach(() => { + fixture = TestBed.createComponent(FileDescriptionComponent); + component = fixture.componentInstance; + component.fileInput = createFileInput(); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display the file name', () => { + const fileNameElement = fixture.debugElement.query( + By.css('.file-content dd') + ).nativeElement; + expect(fileNameElement.textContent).toContain('testFile'); + }); - component.fileInput = fileInput; + it('should not show the embargo badge', () => { + const badge = fixture.debugElement.query(By.css('span.badge')); + expect(badge).toBeNull(); + }); - fixture.detectChanges(); + it('should not show the lock icon', () => { + const lockIcon = fixture.debugElement.query(By.css('.file-content dd i.fa-lock')); + expect(lockIcon).toBeNull(); + }); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('when the bitstream is embargoed and the feature flag is on', () => { + beforeEach(() => { + environment.item.bitstream.showAccessStatuses = true; + fixture = TestBed.createComponent(FileDescriptionComponent); + component = fixture.componentInstance; + component.fileInput = createFileInput({ status: 'embargo', embargoDate: '2050-01-01' }); + fixture.detectChanges(); + }); + + afterEach(() => { + environment.item.bitstream.showAccessStatuses = false; + }); + + it('should show the embargo badge', () => { + const badge = fixture.debugElement.query(By.css('span.badge')); + expect(badge).not.toBeNull(); + expect(badge.nativeElement.textContent).toContain('embargo.listelement.badge'); + }); + + it('should show the lock icon next to the file name', () => { + const lockIcon = fixture.debugElement.query(By.css('.file-content dd i.fa-lock')); + expect(lockIcon).not.toBeNull(); + }); + + it('should render the file name as a clickable link, matching the ZCU-PUB pattern', () => { + const link = fixture.debugElement.query(By.css('.file-content dd a')); + expect(link).not.toBeNull(); + expect(link.nativeElement.textContent).toContain('testFile'); + }); }); - it('should display the file name', () => { - const fileNameElement = fixture.debugElement.query( - By.css('.file-content dd') - ).nativeElement; - expect(fileNameElement.textContent).toContain('testFile'); + describe('when the bitstream is embargoed but the feature flag is off', () => { + beforeEach(() => { + fixture = TestBed.createComponent(FileDescriptionComponent); + component = fixture.componentInstance; + component.fileInput = createFileInput({ status: 'embargo', embargoDate: '2050-01-01' }); + fixture.detectChanges(); + }); + + it('should not show the embargo badge', () => { + const badge = fixture.debugElement.query(By.css('span.badge')); + expect(badge).toBeNull(); + }); + + it('should not show the lock icon', () => { + const lockIcon = fixture.debugElement.query(By.css('.file-content dd i.fa-lock')); + expect(lockIcon).toBeNull(); + }); }); }); diff --git a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts index cb2a2bf2ed1..ee754501013 100644 --- a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts +++ b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.ts @@ -2,6 +2,7 @@ import { Component, Input } from '@angular/core'; import { MetadataBitstream } from 'src/app/core/metadata/metadata-bitstream.model'; import { HALEndpointService } from '../../../../../core/shared/hal-endpoint.service'; import {Router} from '@angular/router'; +import { environment } from 'src/environments/environment'; const allowedPreviewFormats = ['text/plain', 'text/html', 'application/zip']; @Component({ @@ -13,6 +14,14 @@ export class FileDescriptionComponent { MIME_TYPE_IMAGES_PATH = '/assets/images/mime/'; MIME_TYPE_DEFAULT_IMAGE_NAME = 'application-octet-stream.png'; + /** + * Whether to show the embargo-date badge for a restricted bitstream. Same feature flag as + * the standard file-download-link/AccessStatusBadgeComponent path on other customer instances. + */ + get showAccessStatus(): boolean { + return environment.item.bitstream.showAccessStatuses; + } + @Input() fileInput: MetadataBitstream; diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5 index 4d0ec104948..9ae830d2672 100644 --- a/src/assets/i18n/cs.json5 +++ b/src/assets/i18n/cs.json5 @@ -8397,4 +8397,6 @@ // "item.page.cc.license.disclaimer": "Except where otherwised noted, this item's license is described as", // TODO New key - Add a translation "item.page.cc.license.disclaimer": "Except where otherwised noted, this item's license is described as", + // "embargo.listelement.badge": "Embargo until {{ date }}", + "embargo.listelement.badge": "Embargo do {{ date }}", } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 1ae3aa647db..062f3e6f876 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -5930,4 +5930,6 @@ "navbar.about.service-integrations": "Service integrations", "navbar.about.project-partnership": "Project partnerships", + + "embargo.listelement.badge": "Embargo until {{ date }}", } diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 6f05dedb112..e1578b2da74 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -285,7 +285,10 @@ export class DefaultAppConfig implements AppConfig { // Number of entries in the bitstream list in the item view page. // Rounded to the nearest size in the list of selectable sizes on the // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. - pageSize: 5 + pageSize: 5, + // Show the bitstream embargo-date badge. + // Keep this false until the backend accessStatus support (DSpace#1380) is deployed. + showAccessStatuses: false } }; diff --git a/src/config/item-config.interface.ts b/src/config/item-config.interface.ts index 35cb5260aea..f3141f77238 100644 --- a/src/config/item-config.interface.ts +++ b/src/config/item-config.interface.ts @@ -12,5 +12,7 @@ export interface ItemConfig extends Config { // Rounded to the nearest size in the list of selectable sizes on the // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. pageSize: number; + // Show the bitstream embargo-date badge + showAccessStatuses: boolean; } } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 8a6a5d1fccb..46164ae5da4 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -254,7 +254,9 @@ export const environment: BuildConfig = { // Number of entries in the bitstream list in the item view page. // Rounded to the nearest size in the list of selectable sizes on the // settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. - pageSize: 5 + pageSize: 5, + // Show the bitstream embargo-date badge + showAccessStatuses: false } }, collection: {