From 01738185e126b4e05397a071c520d742f621c4dd Mon Sep 17 00:00:00 2001 From: Matus Kasak Date: Mon, 6 Jul 2026 14:15:29 +0200 Subject: [PATCH 1/3] JCU/Port CitacePRO citation widget from customer/mendelu Adds the CitacePRO citation widget to the item page for JCU, mirroring the customer/mendelu implementation. - New standalone ItemPageCitationFieldComponent (ds-item-page-citation-field): reads citace.pro.url / citace.pro.university / citace.pro.allowed via ConfigurationDataService and embeds the CitacePRO iframe (::) with a disclaimer, shown only when citace.pro.allowed = true. - Wired into publication + untyped-item item views (base app and themes/custom). - Added item.page.citace-pro.disclaimer to en/cs i18n. Ported from dataquest-dev/dspace-angular #1161 (initial component + wiring), #1166 (always-render fix), #1167 (only the citation spec tweak, not the unrelated bundled UI files), and #1264 (disclaimer text + i18n). Requires the backend citace.pro.* config exposed via REST (dataquest-dev/DSpace feature/jcu-citacepro). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../item-page-citation.component.html | 14 ++++ .../item-page-citation.component.scss | 3 + .../item-page-citation.component.spec.ts | 64 +++++++++++++++ .../citation/item-page-citation.component.ts | 82 +++++++++++++++++++ .../publication/publication.component.html | 1 + .../publication/publication.component.ts | 2 + .../untyped-item/untyped-item.component.html | 1 + .../untyped-item/untyped-item.component.ts | 2 + src/assets/i18n/cs.json5 | 3 + src/assets/i18n/en.json5 | 2 + .../publication/publication.component.ts | 2 + .../untyped-item/untyped-item.component.ts | 2 + 12 files changed, 178 insertions(+) create mode 100644 src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html create mode 100644 src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.scss create mode 100644 src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.spec.ts create mode 100644 src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html new file mode 100644 index 00000000000..76c4c5fe41f --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html @@ -0,0 +1,14 @@ +@if ((citaceProStatus$ | async) && (citaceProURL$ | async); as citaceProURL) { +
+ +

+ {{ 'item.page.citace-pro.disclaimer' | translate }} +

+
+} diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.scss b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.scss new file mode 100644 index 00000000000..75bc6114493 --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.scss @@ -0,0 +1,3 @@ +/** +This is styling file for the component `item-page-citation.component`. + */ diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.spec.ts b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.spec.ts new file mode 100644 index 00000000000..7a9c53c08a0 --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.spec.ts @@ -0,0 +1,64 @@ +import { + ChangeDetectionStrategy, + NO_ERRORS_SCHEMA, +} from '@angular/core'; +import { + ComponentFixture, + TestBed, + waitForAsync, +} from '@angular/core/testing'; +import { 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'; + +describe('ItemPageCitationFieldComponent', () => { + let component: ItemPageCitationFieldComponent; + let fixture: ComponentFixture; + let sanitizer: DomSanitizer; + const mockHandle = '123456789/3'; + let mockConfigurationDataService: ConfigurationDataService; + + mockConfigurationDataService = jasmine.createSpyObj('configurationDataService', { + findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), { + name: 'property', + values: [ + 'value', + ], + })), + }); + + beforeEach(waitForAsync(() => { + void TestBed.configureTestingModule({ + imports: [ItemPageCitationFieldComponent], + providers: [ + { provide: ConfigurationDataService, useValue: mockConfigurationDataService }, + ], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(ItemPageCitationFieldComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default }, + }) + .compileComponents(); + + sanitizer = TestBed.inject(DomSanitizer); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ItemPageCitationFieldComponent); + component = fixture.componentInstance; + component.handle = mockHandle; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize citaceProURL$ and citaceProStatus$', () => { + expect(component.citaceProURL$).toBeDefined(); + expect(component.citaceProStatus$).toBeDefined(); + }); +}); diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts new file mode 100644 index 00000000000..957abd56222 --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts @@ -0,0 +1,82 @@ +import { CommonModule } from '@angular/common'; +import { + Component, + Input, + OnInit, +} from '@angular/core'; +import { + DomSanitizer, + SafeResourceUrl, +} from '@angular/platform-browser'; +import { TranslateModule } from '@ngx-translate/core'; +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, + TranslateModule, + ], +}) +export class ItemPageCitationFieldComponent implements OnInit { + @Input() handle: string; + + citaceProStatus$ = new BehaviorSubject(false); + citaceProURL$ = new BehaviorSubject(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; + } + const url = `${citaceProBaseUrl}:${universityUsingDspace}:${this.handle}`; + return this.sanitizer.bypassSecurityTrustResourceUrl(url); + } +} diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index a294cc025bc..4ad2eb72584 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -70,6 +70,7 @@ [relationType]="'isJournalIssueOfPublication'" [label]="'item.page.journal-issue' | translate"> +
+ Date: Mon, 6 Jul 2026 15:05:10 +0200 Subject: [PATCH 2/3] JCU/Remove mendelu-specific CitacePRO disclaimer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "These citations were generated by software ... ČSN ISO 690" disclaimer shown under the CitacePRO iframe is mendelu-specific and not wanted for JCU. - Remove the

disclaimer from item-page-citation.component.html - Drop the now-unused TranslateModule import from the component - Remove item.page.citace-pro.disclaimer from en.json5 and cs.json5 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../specific-field/citation/item-page-citation.component.html | 3 --- .../specific-field/citation/item-page-citation.component.ts | 2 -- src/assets/i18n/cs.json5 | 3 --- src/assets/i18n/en.json5 | 2 -- 4 files changed, 10 deletions(-) diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html index 76c4c5fe41f..61e23b1fba3 100644 --- a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html @@ -7,8 +7,5 @@ style="border: none;" [src]="citaceProURL" > -

- {{ 'item.page.citace-pro.disclaimer' | translate }} -

} diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts index 957abd56222..d4e4995fb0a 100644 --- a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.ts @@ -8,7 +8,6 @@ import { DomSanitizer, SafeResourceUrl, } from '@angular/platform-browser'; -import { TranslateModule } from '@ngx-translate/core'; import { BehaviorSubject, combineLatest, @@ -27,7 +26,6 @@ import { getFirstCompletedRemoteData } from '../../../../../core/shared/operator templateUrl: './item-page-citation.component.html', imports: [ CommonModule, - TranslateModule, ], }) export class ItemPageCitationFieldComponent implements OnInit { diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5 index 5f808811858..9eb64a1338c 100644 --- a/src/assets/i18n/cs.json5 +++ b/src/assets/i18n/cs.json5 @@ -4401,9 +4401,6 @@ // "item.page.authors": "Authors", "item.page.authors": "Autoři", - // "item.page.citace-pro.disclaimer": "These citations were generated by software and may contain errors. The citation provided may not comply with the ČSN ISO 690 standard.", - "item.page.citace-pro.disclaimer": "Tyto citace vytvořil software a mohou obsahovat chyby. Uvedená citace nemusí být v souladu s normou ČSN ISO 690.", - // "item.page.citation": "Citation", "item.page.citation": "Citace", diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 2c39a6b0c42..79f4b0f6b60 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2919,8 +2919,6 @@ "item.page.authors": "Authors", - "item.page.citace-pro.disclaimer": "These citations were generated by software and may contain errors. The citation provided may not comply with the ČSN ISO 690 standard.", - "item.page.citation": "Citation", "item.page.collections": "Collections", From 858e85383f47f0f2d671dc12c9b543cdeded78d4 Mon Sep 17 00:00:00 2001 From: Matus Kasak Date: Mon, 6 Jul 2026 15:53:16 +0200 Subject: [PATCH 3/3] JCU/CitacePRO: fix Publication specs, review findings, iframe gap Address the failing CI and the Copilot review on PR #1356: - publication.component.spec.ts: provide ConfigurationDataServiceStub (same pattern as the passing untyped-item spec). PublicationComponent now renders ds-item-page-citation-field, whose ngOnInit called the real ConfigurationDataService against the spec's bare HALEndpointService stub (this.halService.getEndpoint is not a function), failing all 12 PublicationComponent tests on CI. - item-page-citation.component.ts: allow only http(s) base URLs before bypassSecurityTrustResourceUrl, so a bad citace.pro.url config value (e.g. javascript:) can never be trusted as iframe src. - item-page-citation.component.html: add an accessibility title to the iframe, and shrink the wrapper spacing mb-5 pb-4 -> mb-2. The large spacing was inherited from mendelu where a disclaimer paragraph sat under the iframe; JCU dropped the disclaimer, which left ~72px of empty space between the citation and the abstract. Iframe untouched. - item-page-citation.component.spec.ts: name-based config mock and behavior assertions (allowed=true -> URL composed + iframe rendered with title; allowed=false -> hidden; non-http(s)/missing config -> null), replacing the existence-only checks. - item-page-citation.component.scss: removed; it contained only a comment and was never referenced by the component (styleUrls). Co-Authored-By: Claude Fable 5 --- .../item-page-citation.component.html | 3 +- .../item-page-citation.component.scss | 3 - .../item-page-citation.component.spec.ts | 94 ++++++++++++++----- .../citation/item-page-citation.component.ts | 5 + .../publication/publication.component.spec.ts | 3 + 5 files changed, 79 insertions(+), 29 deletions(-) delete mode 100644 src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.scss diff --git a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html index 61e23b1fba3..ae7bad867a7 100644 --- a/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html +++ b/src/app/item-page/simple/field-components/specific-field/citation/item-page-citation.component.html @@ -1,7 +1,8 @@ @if ((citaceProStatus$ | async) && (citaceProURL$ | async); as citaceProURL) { -
+