- @for (attachmentConf of metadataConfig; track $index) {
+ @for (attachmentConf of (visibleMetadataConfig$ | async); track $index) {
@if (attachment.firstMetadataValue(attachmentConf.name) || attachmentConf.type === AdvancedAttachmentElementType.Attribute) {
{{ 'layout.advanced-attachment.' + attachmentConf.name | translate }}
diff --git a/src/app/shared/bitstream-attachment/bitstream-attachment.component.spec.ts b/src/app/shared/bitstream-attachment/bitstream-attachment.component.spec.ts
index 63a7d59820b..833f2f4b9b7 100644
--- a/src/app/shared/bitstream-attachment/bitstream-attachment.component.spec.ts
+++ b/src/app/shared/bitstream-attachment/bitstream-attachment.component.spec.ts
@@ -6,6 +6,7 @@ import {
import { provideRouter } from '@angular/router';
import { APP_CONFIG } from '@dspace/config/app-config.interface';
import { BitstreamDataService } from '@dspace/core/data/bitstream-data.service';
+import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service';
import { LocaleService } from '@dspace/core/locale/locale.service';
import { Bitstream } from '@dspace/core/shared/bitstream.model';
import { Item } from '@dspace/core/shared/item.model';
@@ -42,8 +43,13 @@ describe('BitstreamAttachmentComponent', () => {
getCurrentLanguageCode: of('en'),
getLanguageCodeList: of(languageList),
});
+ let authorizationService: jasmine.SpyObj
;
beforeEach(async () => {
+ authorizationService = jasmine.createSpyObj('AuthorizationDataService', {
+ isAuthorized: of(true),
+ });
+
await TestBed.configureTestingModule({
imports: [
BitstreamAttachmentComponent,
@@ -60,6 +66,7 @@ describe('BitstreamAttachmentComponent', () => {
{ provide: BitstreamDataService, useValue: {} },
{ provide: APP_CONFIG, useValue: environment },
{ provide: LocaleService, useValue: mockLocaleService },
+ { provide: AuthorizationDataService, useValue: authorizationService },
],
schemas: [NO_ERRORS_SCHEMA],
})
@@ -112,4 +119,25 @@ describe('BitstreamAttachmentComponent', () => {
const badge = fixture.nativeElement.querySelector('.badge.bg-primary');
expect(badge).toBeNull();
});
+
+ it('should display admin-only elements (checksum) for administrators', () => {
+ authorizationService.isAuthorized.and.returnValue(of(true));
+ component.ngOnInit();
+ fixture.detectChanges();
+ expect(fixture.nativeElement.querySelector('[data-test="checksum"]')).toBeTruthy();
+ });
+
+ it('should hide admin-only elements (checksum) from non-administrators', () => {
+ authorizationService.isAuthorized.and.returnValue(of(false));
+ component.ngOnInit();
+ fixture.detectChanges();
+ expect(fixture.nativeElement.querySelector('[data-test="checksum"]')).toBeNull();
+ });
+
+ it('should always display public elements (format) regardless of admin status', () => {
+ authorizationService.isAuthorized.and.returnValue(of(false));
+ component.ngOnInit();
+ fixture.detectChanges();
+ expect(fixture.nativeElement.querySelector('[data-test="format"]')).toBeTruthy();
+ });
});
diff --git a/src/app/shared/bitstream-attachment/bitstream-attachment.component.ts b/src/app/shared/bitstream-attachment/bitstream-attachment.component.ts
index 64abe115d97..d42eae811a1 100644
--- a/src/app/shared/bitstream-attachment/bitstream-attachment.component.ts
+++ b/src/app/shared/bitstream-attachment/bitstream-attachment.component.ts
@@ -14,6 +14,7 @@ import {
} from '@angular/router';
import {
AdvancedAttachmentElementType,
+ AdvancedAttachmentVisibility,
AttachmentMetadataConfig,
} from '@dspace/config/advanced-attachment-rendering.config';
import {
@@ -21,6 +22,8 @@ import {
AppConfig,
} from '@dspace/config/app-config.interface';
import { BitstreamDataService } from '@dspace/core/data/bitstream-data.service';
+import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service';
+import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
import { RemoteData } from '@dspace/core/data/remote-data';
import {
Bitstream,
@@ -69,6 +72,12 @@ export class BitstreamAttachmentComponent implements OnInit {
*/
metadataConfig: AttachmentMetadataConfig[];
+ /**
+ * The configured attachment elements that the current user is allowed to see.
+ * Elements with visibility "admin" are only emitted for site administrators.
+ */
+ visibleMetadataConfig$: Observable;
+
/**
* All item providers to show buttons of
*/
@@ -120,6 +129,7 @@ export class BitstreamAttachmentComponent implements OnInit {
protected readonly translateService: TranslateService,
protected readonly router: Router,
protected readonly route: ActivatedRoute,
+ protected readonly authorizationService: AuthorizationDataService,
@Inject(APP_CONFIG) protected appConfig: AppConfig,
) {
this.metadataConfig = this.appConfig.layout.advancedAttachmentRendering.metadata;
@@ -131,6 +141,11 @@ export class BitstreamAttachmentComponent implements OnInit {
).subscribe((thumbnail: RemoteData) => {
this.thumbnail$.next(thumbnail);
});
+ this.visibleMetadataConfig$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf).pipe(
+ map((isAdmin: boolean) => this.metadataConfig.filter(
+ (config: AttachmentMetadataConfig) => config.visibility !== AdvancedAttachmentVisibility.Admin || isAdmin,
+ )),
+ );
this.allAttachmentProviders = this.attachment?.allMetadataValues('bitstream.viewer.provider');
this.bitstreamFormat$ = this.getFormat(this.attachment);
this.bitstreamSize = this.getSize(this.attachment);
diff --git a/src/config/advanced-attachment-rendering.config.ts b/src/config/advanced-attachment-rendering.config.ts
index cbbf4a6f9d0..53de2ad0337 100644
--- a/src/config/advanced-attachment-rendering.config.ts
+++ b/src/config/advanced-attachment-rendering.config.ts
@@ -12,6 +12,12 @@ export interface AttachmentMetadataConfig {
name: string;
type: AdvancedAttachmentElementType;
truncatable?: boolean;
+ /**
+ * Controls who can see this attachment element.
+ * When omitted, the element is treated as {@link AdvancedAttachmentVisibility.Public}.
+ * To hide an element from everyone, remove it from the metadata list entirely.
+ */
+ visibility?: AdvancedAttachmentVisibility;
}
/**
@@ -22,3 +28,17 @@ export enum AdvancedAttachmentElementType {
Attribute = 'attribute'
}
+/**
+ * Controls the audience that can see an advanced attachment element.
+ */
+export enum AdvancedAttachmentVisibility {
+ /**
+ * Visible to everyone, including anonymous users (default when no visibility is configured).
+ */
+ Public = 'public',
+ /**
+ * Visible only to site administrators.
+ */
+ Admin = 'admin'
+}
+
diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts
index fd703d4d024..c3648dfcd8b 100644
--- a/src/config/default-app-config.ts
+++ b/src/config/default-app-config.ts
@@ -5,7 +5,10 @@ import { AccessibilitySettingsConfig } from './accessibility-settings.config';
import { ActuatorsConfig } from './actuators.config';
import { AddToAnyPluginConfig } from './add-to-any-plugin-config';
import { AdminNotifyMetricsRow } from './admin-notify-metrics.config';
-import { AdvancedAttachmentElementType } from './advanced-attachment-rendering.config';
+import {
+ AdvancedAttachmentElementType,
+ AdvancedAttachmentVisibility,
+} from './advanced-attachment-rendering.config';
import { AppConfig } from './app-config.interface';
import { AuthConfig } from './auth-config.interfaces';
import { BrowseByConfig } from './browse-by-config.interface';
@@ -812,6 +815,7 @@ export class DefaultAppConfig implements AppConfig {
{
name: 'checksum',
type: AdvancedAttachmentElementType.Attribute,
+ visibility: AdvancedAttachmentVisibility.Admin,
},
],
},
diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts
index 0ae82e957a1..90ceee67d0c 100644
--- a/src/environments/environment.test.ts
+++ b/src/environments/environment.test.ts
@@ -1,5 +1,8 @@
// This configuration is only used for unit tests, end-to-end tests use environment.production.ts
-import { AdvancedAttachmentElementType } from '@dspace/config/advanced-attachment-rendering.config';
+import {
+ AdvancedAttachmentElementType,
+ AdvancedAttachmentVisibility,
+} from '@dspace/config/advanced-attachment-rendering.config';
import { NotificationAnimationsType } from '@dspace/config/notifications-config.interfaces';
import { RestRequestMethod } from '@dspace/config/rest-request-method';
import { BuildConfig } from 'src/config/build-config.interface';
@@ -600,6 +603,7 @@ export const environment: BuildConfig = {
{
name: 'checksum',
type: AdvancedAttachmentElementType.Attribute,
+ visibility: AdvancedAttachmentVisibility.Admin,
},
],
},