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
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@if ((citaceProStatus$ | async) && (citaceProURL$ | async); as citaceProURL) {
<div class="mb-2">
<iframe
class="d-block"
title="Citace PRO"
height="150px"
width="100%"
style="border: none;"
[src]="citaceProURL"
></iframe>
Comment thread
Kasinhou marked this conversation as resolved.
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
ChangeDetectionStrategy,
NO_ERRORS_SCHEMA,
SecurityContext,
} from '@angular/core';
import {
ComponentFixture,
TestBed,
} from '@angular/core/testing';
import {
By,
DomSanitizer,
} from '@angular/platform-browser';

import { ConfigurationDataService } from '../../../../../core/data/configuration-data.service';
import { ConfigurationProperty } from '../../../../../core/shared/configuration-property.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
import { ItemPageCitationFieldComponent } from './item-page-citation.component';

const CITACE_PRO_URL = 'https://www.citacepro.com/api/dspace/citace/oai';
const CITACE_PRO_UNIVERSITY = 'dspace.jcu.cz';

describe('ItemPageCitationFieldComponent', () => {
let component: ItemPageCitationFieldComponent;
let fixture: ComponentFixture<ItemPageCitationFieldComponent>;
const mockHandle = '123456789/3';

function mockConfigurationDataService(allowed: string) {
const valuesByName: { [name: string]: string[] } = {
'citace.pro.url': [CITACE_PRO_URL],
'citace.pro.university': [CITACE_PRO_UNIVERSITY],
'citace.pro.allowed': [allowed],
};
return {
findByPropertyName: (name: string) => createSuccessfulRemoteDataObject$(
Object.assign(new ConfigurationProperty(), { name, values: valuesByName[name] ?? [] }),
),
};
}

async function init(allowed: string): Promise<void> {
await TestBed.configureTestingModule({
imports: [ItemPageCitationFieldComponent],
providers: [
{ provide: ConfigurationDataService, useValue: mockConfigurationDataService(allowed) },
],
schemas: [NO_ERRORS_SCHEMA],
})
.overrideComponent(ItemPageCitationFieldComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default },
})
.compileComponents();

fixture = TestBed.createComponent(ItemPageCitationFieldComponent);
component = fixture.componentInstance;
component.handle = mockHandle;
fixture.detectChanges();
}

describe('when citace.pro.allowed is true', () => {
beforeEach(async () => {
await init('true');
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should enable the widget and compose the CitacePRO URL', () => {
expect(component.citaceProStatus$.getValue()).toBeTrue();
const sanitizer = TestBed.inject(DomSanitizer);
expect(sanitizer.sanitize(SecurityContext.RESOURCE_URL, component.citaceProURL$.getValue()))
.toBe(`${CITACE_PRO_URL}:${CITACE_PRO_UNIVERSITY}:${mockHandle}`);
});

it('should render the iframe with a title', () => {
const iframe = fixture.debugElement.query(By.css('iframe'));
expect(iframe).not.toBeNull();
expect(iframe.nativeElement.getAttribute('title')).toBe('Citace PRO');
});
});

describe('when citace.pro.allowed is false', () => {
beforeEach(async () => {
await init('false');
});

it('should keep the widget hidden', () => {
expect(component.citaceProStatus$.getValue()).toBeFalse();
expect(fixture.debugElement.query(By.css('iframe'))).toBeNull();
});
});

describe('makeCitaceProURL', () => {
beforeEach(async () => {
await init('true');
});

it('should reject a non-http(s) base URL', () => {
expect(component.makeCitaceProURL('javascript:alert(1)//', CITACE_PRO_UNIVERSITY)).toBeNull();
});

it('should reject missing config values', () => {
expect(component.makeCitaceProURL(undefined, CITACE_PRO_UNIVERSITY)).toBeNull();
expect(component.makeCitaceProURL(CITACE_PRO_URL, undefined)).toBeNull();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { CommonModule } from '@angular/common';
import {
Component,
Input,
OnInit,
} from '@angular/core';
import {
DomSanitizer,
SafeResourceUrl,
} from '@angular/platform-browser';
import {
BehaviorSubject,
combineLatest,
of,
} from 'rxjs';
import {
catchError,
take,
} from 'rxjs/operators';

import { ConfigurationDataService } from '../../../../../core/data/configuration-data.service';
import { getFirstCompletedRemoteData } from '../../../../../core/shared/operators';

@Component({
selector: 'ds-item-page-citation-field',
templateUrl: './item-page-citation.component.html',
imports: [
CommonModule,
],
})
Comment thread
Kasinhou marked this conversation as resolved.
export class ItemPageCitationFieldComponent implements OnInit {
@Input() handle: string;

citaceProStatus$ = new BehaviorSubject<boolean>(false);
citaceProURL$ = new BehaviorSubject<SafeResourceUrl | null>(null);

constructor(
private sanitizer: DomSanitizer,
private configService: ConfigurationDataService,
) {}


ngOnInit() {
const citaceProUrl$ = this.configService.findByPropertyName('citace.pro.url').pipe(
getFirstCompletedRemoteData(),
catchError(() => of(null)),
);
const universityUsingDspace$ = this.configService.findByPropertyName('citace.pro.university').pipe(
getFirstCompletedRemoteData(),
catchError(() => of(null)),
);
const citaceProAllowed$ = this.configService.findByPropertyName('citace.pro.allowed').pipe(
getFirstCompletedRemoteData(),
catchError(() => of(null)),
);

combineLatest([citaceProUrl$, universityUsingDspace$, citaceProAllowed$]).pipe(
take(1),
).subscribe(([citaceProUrlData, universityData, citaceProAllowedData]) => {
const citaceProBaseUrl = citaceProUrlData?.payload?.values?.[0];
const universityUsingDspace = universityData?.payload?.values?.[0];
this.citaceProURL$.next(this.makeCitaceProURL(citaceProBaseUrl, universityUsingDspace));

const citaceProAllowed = citaceProAllowedData?.payload?.values?.[0];
this.citaceProStatus$.next(citaceProAllowed === 'true');
});
}


makeCitaceProURL(
citaceProBaseUrl: string,
universityUsingDspace: string,
): SafeResourceUrl | null {
if (!citaceProBaseUrl || !universityUsingDspace || !this.handle) {
return null;
}
// Only http(s) bases may bypass Angular's resource-URL sanitization;
// anything else (javascript:, data:, ...) must not be trusted as iframe src.
if (!/^https?:\/\//i.test(citaceProBaseUrl)) {
return null;
}
const url = `${citaceProBaseUrl}:${universityUsingDspace}:${this.handle}`;
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
Comment thread
Kasinhou marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
[relationType]="'isJournalIssueOfPublication'"
[label]="'item.page.journal-issue' | translate">
</ds-related-items>
<ds-item-page-citation-field [handle]="object?.handle"></ds-item-page-citation-field>
<ds-item-page-abstract-field [item]="object"></ds-item-page-abstract-field>
<ds-generic-item-page-field [item]="object"
[fields]="['dc.description']"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-d
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service';
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
Expand All @@ -55,6 +56,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { ThemedResultsBackButtonComponent } from '../../../../shared/results-back-button/themed-results-back-button.component';
import { BrowseDefinitionDataServiceStub } from '../../../../shared/testing/browse-definition-data-service.stub';
import { ConfigurationDataServiceStub } from '../../../../shared/testing/configuration-data.service.stub';
import { createPaginatedList } from '../../../../shared/testing/utils.test';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
Expand Down Expand Up @@ -131,6 +133,7 @@ describe('PublicationComponent', () => {
{ provide: SearchService, useValue: {} },
{ provide: RouteService, useValue: mockRouteService },
{ provide: BrowseDefinitionDataService, useValue: BrowseDefinitionDataServiceStub },
{ provide: ConfigurationDataService, useValue: new ConfigurationDataServiceStub() },
{ provide: APP_CONFIG, useValue: environment },
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ThemedMediaViewerComponent } from '../../../media-viewer/themed-media-v
import { MiradorViewerComponent } from '../../../mirador-viewer/mirador-viewer.component';
import { ThemedFileSectionComponent } from '../../field-components/file-section/themed-file-section.component';
import { ItemPageAbstractFieldComponent } from '../../field-components/specific-field/abstract/item-page-abstract-field.component';
import { ItemPageCitationFieldComponent } from '../../field-components/specific-field/citation/item-page-citation.component';
import { ItemPageDateFieldComponent } from '../../field-components/specific-field/date/item-page-date-field.component';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
Comment thread
Kasinhou marked this conversation as resolved.
import { GeospatialItemPageFieldComponent } from '../../field-components/specific-field/geospatial/geospatial-item-page-field.component';
Expand All @@ -43,6 +44,7 @@ import { ItemComponent } from '../shared/item.component';
GenericItemPageFieldComponent,
GeospatialItemPageFieldComponent,
ItemPageAbstractFieldComponent,
ItemPageCitationFieldComponent,
ItemPageDateFieldComponent,
ItemPageUriFieldComponent,
MetadataFieldWrapperComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
</ds-generic-item-page-field>
</div>
<div class="col-12 col-md-6">
<ds-item-page-citation-field [handle]="object?.handle"></ds-item-page-citation-field>
<ds-item-page-abstract-field [item]="object"></ds-item-page-abstract-field>
<ds-generic-item-page-field [item]="object"
[fields]="['dc.description']"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { MiradorViewerComponent } from '../../../mirador-viewer/mirador-viewer.c
import { ThemedFileSectionComponent } from '../../field-components/file-section/themed-file-section.component';
import { ItemPageAbstractFieldComponent } from '../../field-components/specific-field/abstract/item-page-abstract-field.component';
import { ItemPageCcLicenseFieldComponent } from '../../field-components/specific-field/cc-license/item-page-cc-license-field.component';
import { ItemPageCitationFieldComponent } from '../../field-components/specific-field/citation/item-page-citation.component';
import { ItemPageDateFieldComponent } from '../../field-components/specific-field/date/item-page-date-field.component';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
import { GeospatialItemPageFieldComponent } from '../../field-components/specific-field/geospatial/geospatial-item-page-field.component';
Expand All @@ -45,6 +46,7 @@ import { ItemComponent } from '../shared/item.component';
GeospatialItemPageFieldComponent,
ItemPageAbstractFieldComponent,
ItemPageCcLicenseFieldComponent,
ItemPageCitationFieldComponent,
ItemPageDateFieldComponent,
ItemPageUriFieldComponent,
MetadataFieldWrapperComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ThemedMediaViewerComponent } from '../../../../../../../app/item-page/m
import { MiradorViewerComponent } from '../../../../../../../app/item-page/mirador-viewer/mirador-viewer.component';
import { ThemedFileSectionComponent } from '../../../../../../../app/item-page/simple/field-components/file-section/themed-file-section.component';
import { ItemPageAbstractFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component';
import { ItemPageCitationFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/citation/item-page-citation.component';
import { ItemPageDateFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/date/item-page-date-field.component';
import { GenericItemPageFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { GeospatialItemPageFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/geospatial/geospatial-item-page-field.component';
Expand Down Expand Up @@ -42,6 +43,7 @@ import { ThemedThumbnailComponent } from '../../../../../../../app/thumbnail/the
GenericItemPageFieldComponent,
GeospatialItemPageFieldComponent,
ItemPageAbstractFieldComponent,
ItemPageCitationFieldComponent,
ItemPageDateFieldComponent,
ItemPageUriFieldComponent,
MetadataFieldWrapperComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { MiradorViewerComponent } from '../../../../../../../app/item-page/mirad
import { ThemedFileSectionComponent } from '../../../../../../../app/item-page/simple/field-components/file-section/themed-file-section.component';
import { ItemPageAbstractFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component';
import { ItemPageCcLicenseFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/cc-license/item-page-cc-license-field.component';
import { ItemPageCitationFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/citation/item-page-citation.component';
import { ItemPageDateFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/date/item-page-date-field.component';
import { GenericItemPageFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { GeospatialItemPageFieldComponent } from '../../../../../../../app/item-page/simple/field-components/specific-field/geospatial/geospatial-item-page-field.component';
Expand Down Expand Up @@ -46,6 +47,7 @@ import { ThemedThumbnailComponent } from '../../../../../../../app/thumbnail/the
GeospatialItemPageFieldComponent,
ItemPageAbstractFieldComponent,
ItemPageCcLicenseFieldComponent,
ItemPageCitationFieldComponent,
ItemPageDateFieldComponent,
ItemPageUriFieldComponent,
MetadataFieldWrapperComponent,
Expand Down
Loading