Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,10 @@ layout:
showDownloadLinkAsAttachment: false
# Configuration for advanced attachment rendering in item pages. This controls how files are displayed when showDownloadLinkAsAttachment is enabled.
# Defines which metadata/attributes to display for bitstream attachments.
# Each element may set an optional "visibility" controlling who can see it:
# public (default when omitted) - visible to everyone, including anonymous users
# admin - visible only to site administrators
# To hide an element from everyone, remove it from the metadata list entirely.
advancedAttachmentRendering:
# Metadata and attributes to display for each attachment
metadata:
Expand All @@ -756,6 +760,7 @@ layout:
type: attribute
- name: checksum
type: attribute
visibility: admin

# Configuration for customization of search results
searchResults:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</div>

<div class="order-lg-2 w-100 mb-3">
@for (attachmentConf of metadataConfig; track $index) {
@for (attachmentConf of (visibleMetadataConfig$ | async); track $index) {
@if (attachment.firstMetadataValue(attachmentConf.name) || attachmentConf.type === AdvancedAttachmentElementType.Attribute) {
<div class="content" [attr.data-test]="attachmentConf.name">
<strong>{{ 'layout.advanced-attachment.' + attachmentConf.name | translate }}</strong>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -42,8 +43,13 @@ describe('BitstreamAttachmentComponent', () => {
getCurrentLanguageCode: of('en'),
getLanguageCodeList: of(languageList),
});
let authorizationService: jasmine.SpyObj<AuthorizationDataService>;

beforeEach(async () => {
authorizationService = jasmine.createSpyObj('AuthorizationDataService', {
isAuthorized: of(true),
});

await TestBed.configureTestingModule({
imports: [
BitstreamAttachmentComponent,
Expand All @@ -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],
})
Expand Down Expand Up @@ -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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ import {
} from '@angular/router';
import {
AdvancedAttachmentElementType,
AdvancedAttachmentVisibility,
AttachmentMetadataConfig,
} from '@dspace/config/advanced-attachment-rendering.config';
import {
APP_CONFIG,
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,
Expand Down Expand Up @@ -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<AttachmentMetadataConfig[]>;

/**
* All item providers to show buttons of
*/
Expand Down Expand Up @@ -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;
Expand All @@ -131,6 +141,11 @@ export class BitstreamAttachmentComponent implements OnInit {
).subscribe((thumbnail: RemoteData<Bitstream>) => {
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);
Expand Down
20 changes: 20 additions & 0 deletions src/config/advanced-attachment-rendering.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -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'
}

6 changes: 5 additions & 1 deletion src/config/default-app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -812,6 +815,7 @@ export class DefaultAppConfig implements AppConfig {
{
name: 'checksum',
type: AdvancedAttachmentElementType.Attribute,
visibility: AdvancedAttachmentVisibility.Admin,
},
],
},
Expand Down
6 changes: 5 additions & 1 deletion src/environments/environment.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -600,6 +603,7 @@ export const environment: BuildConfig = {
{
name: 'checksum',
type: AdvancedAttachmentElementType.Attribute,
visibility: AdvancedAttachmentVisibility.Admin,
},
],
},
Expand Down
Loading