ZCU-PUB/Show a per-bitstream embargo-date badge in the Item View - #1378
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Improves Item View bitstream download accessibility by adding descriptive accessible labels for restricted/embargoed bitstreams and introducing i18n keys for those labels.
Changes:
- Adds an
aria-labelto the bitstream download link that includes the bitstream name. - Adds an accessible restricted-state label for the lock icon.
- Adds i18n keys for the new labels (EN + CS) and updates the unit test module to include
TranslateModule.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/app/shared/file-download-link/file-download-link.component.html | Adds aria-labels/keyboard semantics for the download link and restricted lock indicator |
| src/app/shared/file-download-link/file-download-link.component.ts | Injects DSONameService for bitstream name usage in accessible labels |
| src/app/shared/file-download-link/file-download-link.component.spec.ts | Adds TranslateModule.forRoot() to support translate pipe in the template |
| src/assets/i18n/en.json5 | Adds new English i18n keys for download/restricted labels |
| src/assets/i18n/cs.json5 | Adds/updates Czech i18n keys for download/restricted labels |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Make the space between the translated "Download" prefix and the bitstream name explicit in the template instead of relying on a trailing space in the en.json5 source string (cs.json5 had none, producing "Stáhnoutname.pdf" with no separator). - aria-label on a plain <span> is not reliably exposed by assistive tech; switch to the codebase's existing sr-only pattern (see view-mode-switch.component.html) and mark the decorative lock icon aria-hidden. - Add spec coverage asserting the download link's aria-label and the restricted-state sr-only text/aria-hidden icon.
|
Addressed all 3 Copilot review comments in 945ad93:
Re-verified locally: lint ✅, circular-deps ✅, production build ✅ (no budget warnings), |
Local end-to-end verification of the accessibility fix: full item page and a close-up of the restricted-bitstream lock icon, seeded with an embargoed bitstream. For PR review reference only — will be dropped before merge.
|
Scope correction: the earlier commits only added an accessible-name (screen-reader-only) label to the lock icon — that's a real accessibility improvement, but it doesn't surface any visible embargo information, which is what #823 actually asks for. Added in 40b07c8: the item view (publication + untyped-item, both
Re-verified: lint ✅, 64/64 relevant specs ✅ (including publication/untyped-item components), production build ✅ (no budget warnings). |
|
The two metadata fields this PR renders (`dc.description.embargo`, `dc.date.embargoend`) are not registered anywhere in the DSpace backend codebase — not in `dublin-core-types.xml`/`local-types.xml` on `customer/zcu-pub`, and not on `customer/vsb-tuo` either (checked both, where #1258 already ships this same display). They must have been registered manually on the VSB-TUO instance via Administer → Registries → Metadata (or the REST API) after deploy — it's an operational step, not something this frontend PR or any backend migration currently handles. Before/when this ships to ZCU-PUB, someone needs to register on the backend:
Confirmed via local testing: patching an item with these fields fails with 400 until they're registered; once registered, the frontend renders them correctly (see screenshot in the earlier comment above... will follow up with the visible-text screenshot too). Happy to open a companion PR on the backend repo to register these via a proper migration instead of a manual admin step, if that's preferred — let me know. |
|
Visible-text screenshot for commit 40b07c8 (after manually registering the two metadata fields on the test backend — see deployment note above): Rendered text (English UI):
This is real page content (not aria-only), confirming the fix now surfaces actual embargo information as #823 asks for. |
Scope correction, round 2: the real fixFurther review (thanks for catching this) surfaced that neither the earlier accessibility fix nor the item-level metadata fields actually matched what #823 asks for: a per-bitstream embargo badge sourced from that bitstream's own resource policy, not item metadata. Reverted (b01c0e6): the item-level Added (2ea2af5, 5f13be6): the real vanilla DSpace feature, ported from upstream commit 5f83139fcd (present on this fork's DSpace 9.x lines, e.g.
UX pass (5f13be6) after a design review: the date is now formatted with Angular's Screenshot (Files section, anonymous user, after the fixes above): Renders: "Available from June 30, 2027". Backend dependencyThis needs a companion backend change — Verification
|
|
Companion backend PR: dataquest-dev/DSpace#1377 (needed for this badge to actually render data — this FE PR fails closed without it). |
Fixes dataquest-dev/dspace-customers#823 (bitstream portion). Ports the real vanilla DSpace feature that already exists on this fork's DSpace 9.x lines (e.g. customer/mendelu): a unified AccessStatusBadgeComponent that renders an access-status badge for either an Item or a Bitstream, instead of a separate embargo-only component (an earlier draft of this PR introduced a standalone EmbargoBadgeComponent copied from a since-abandoned upstream design (PR dataquest-dev/dspace-angular#3882 / commit 5f83139); that component has no live upstream reference on 9.3/main/mendelu and has been removed in favor of this unified approach). Change set: - Bitstream model gains an `accessStatus` HAL link (mirrors the existing thumbnail/format link shape). - AccessStatusObject (both the canonical copy and a pre-existing duplicate under object-list/) gains `embargoDate`. - AccessStatusBadgeComponent's `object` input widens from `Item` to `Item | Bitstream`; it now resolves the accessStatus link lazily via LinkService.resolveLink(..., followLink('accessStatus', { isOptional: true })) instead of eagerly reading `_links.accessStatus.href` directly. isOptional: true is required (unlike upstream, which never needs it because its backend always has the link already) because this backend doesn't expose the bitstream-level link yet - without it, resolveLink() throws for every bitstream instead of failing closed. - file-download-link.component.html renders <ds-themed-access-status-badge [object]="bitstream"> next to each file, gated by the existing item.bitstream.showAccessStatuses flag. - That flag now defaults to false (matching upstream/mendelu) instead of true - flip it once the backend companion PR (DSpace#1377) is deployed, not before. - i18n copy aligned with vanilla/mendelu: "Embargo until {{ date }}" (en) / "Embargo do {{ date }}" (cs). - Removed the unused findBitstreamAccessStatusFor() and the deleted EmbargoBadgeComponent/ThemedEmbargoBadgeComponent + their shared .module.ts registration. The accessibility fix (aria-label/role/sr-only on the lock icon) that was previously bundled into this branch has been split out to its own PR (#1390) for independent review - this branch was rebuilt from scratch off customer/zcu-pub to drop those commits and keep this PR scoped to the embargo-badge feature only. Test evidence: npx ng lint --quiet -> All files pass linting. npx ng build --configuration production -> build succeeded, no budget warnings npx ng test (access-status-badge + file-download-link + thumbnail specs) -> 39/39 SUCCESS, including a regression test that constructs a real Bitstream lacking _links.accessStatus and asserts ngOnInit doesn't throw and the file list renders without a badge. Manual: local Docker stack running the CURRENT (pre-#1377) zcu-pub backend image, showAccessStatuses temporarily forced true - an embargoed item's file list rendered normally, zero console errors, and no accessStatus network request was even attempted (LinkService detects the missing _links key up front).
3f2f38e to
0b61b91
Compare
|
Summary of what changed and why:
The 3 previously-resolved Copilot review threads on this PR are now stale (they reviewed commits that no longer exist in this branch) - a fresh review pass would be appreciated. |
|
Final, working screenshot — earlier attempts in this thread had raw untranslated `embargo.listelement.badge` text due to an unrelated local build-env bug (`NODE_ENV=production` wasn't set when I ran the Angular build manually, so the i18n asset hash baked into the JS bundle didn't match the copied file's actual name → 404 → translation fell back to the raw key). Not a code issue — fixed by using the project's own `yarn build:prod` script, which sets that correctly via `cross-env`. Close-up: Renders exactly as expected: "Embargo until 2027-06-30", same visual style as the existing access-status badge elsewhere on the site. Reviewed against the actual customer ask (not just the diff) - verdict: approved to ship, with two non-blocking notes for a follow-up: (1) locale-format the date (e.g. "30 June 2027" / "30. června 2027") instead of the raw ISO string, (2) consider a short explanation of the "embargo" term for non-academic visitors. Neither blocks merge. |
…1390) * ZCU-PUB/Show embargo information in the bitstream in the Item View * Address Copilot review on #1378: fix a11y label wiring + add coverage - Make the space between the translated "Download" prefix and the bitstream name explicit in the template instead of relying on a trailing space in the en.json5 source string (cs.json5 had none, producing "Stáhnoutname.pdf" with no separator). - aria-label on a plain <span> is not reliably exposed by assistive tech; switch to the codebase's existing sr-only pattern (see view-mode-switch.component.html) and mark the decorative lock icon aria-hidden. - Add spec coverage asserting the download link's aria-label and the restricted-state sr-only text/aria-hidden icon. * Adopt Copilot's .trim() suggestion for the download aria-label Defense-in-depth on top of the explicit-space fix: trims the translated prefix and the final concatenated string, so the label stays correctly spaced even if a translation (any of the ~30 locale files, not just cs) carries stray leading/trailing whitespace. * Address Copilot review: fold restricted state into the link's aria-label An element's aria-label overrides its accessible-name computation entirely, so the nested sr-only "Restricted bitstream" text was never actually announced by assistive tech - the lock icon's aria-hidden state was correct, but the restriction reason was silently dropped. Fixed by composing a single aria-label (via the new getDownloadLinkAriaLabel() method) that includes the restricted state when applicable, instead of relying on descendant sr-only content. Also evaluated Copilot's *ngVar suggestion to avoid the two separate canDownload$ | async subscriptions (aria-label + the lock icon's *ngIf) - reverted it after it broke the routerLink binding in tests: *ngVar destroys and recreates its entire templated content (including the anchor and its RouterLink) on every emission, which would cause the download link to flicker/reset once the authorization check resolves. Kept the two independent `| async` pipes instead, consistent with how bitstreamPath$ is already used twice in this same template. --------- Co-authored-by: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com>
…isting (#1394) * Show a per-bitstream embargo-date badge in the CLARIN file listing Fixes dataquest-dev/dspace-customers#823 (ZCU-DATA portion). ZCU-DATA's Item View file listing doesn't go through the standard file-download-link/AccessStatusBadgeComponent path used on ZCU-PUB (#1378) - it's a separate, CLARIN-specific component chain (clarin-files-section -> preview-section -> file-description) backed by a completely different REST endpoint (metadatabitstreams/search/byHandle) that returns a flat DTO, not a HAL resource with resolvable links. This is new implementation, not a port. Change set: - MetadataBitstream model gains status/embargoDate fields, matching what the backend companion PR (dataquest-dev/DSpace#1380) now embeds directly in that endpoint's response - no separate resolution step needed, unlike the HAL-link approach on ZCU-PUB. - file-description.component.html renders the same visual badge (badge badge-secondary, embargo.listelement.badge i18n key) used on ZCU-PUB, for a consistent look, next to the file name. - New item.bitstream.showAccessStatuses config flag (defaults to false, same reasoning as ZCU-PUB: flip only once the backend PR is deployed). - i18n copy matches ZCU-PUB/vanilla: "Embargo until {{ date }}" (en) / "Embargo do {{ date }}" (cs). Test evidence: npx ng lint --quiet -> All files pass linting. npx ng build --configuration production -> build succeeded, no budget warnings npx ng test (file-description.component.spec.ts) -> 5/5 SUCCESS, including coverage for: badge hidden by default, badge shown when embargoed + flag on, badge hidden when embargoed but flag off. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Show lock icon next to embargoed bitstream name Matches the existing lock-icon pattern used on ZCU-PUB's file-download-link component for restricted bitstreams. * Make embargoed file name a clickable link, matching ZCU-PUB visual style 1:1 The lock icon now inherits link color (blue) by living inside an anchor that triggers downloadFile(), exactly mirroring the file-download-link component's <a><lock/>content</a> structure used on ZCU-PUB. Non-embargoed file names are unaffected (still plain text). --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…Statuses) (#1403) * ZCU-PUB/Enable per-bitstream embargo badge (item.bitstream.showAccessStatuses) The embargo-date badge (dspace-customers#823, FE #1378/#1390) is gated behind item.bitstream.showAccessStatuses, which defaults to false in default-app-config.ts. Set it to true in config.yml so the badge renders on redeploy. Safe now that the backend accessStatus endpoint is deployed for zcu-pub (DSpace#1377); the frontend fails closed without it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Simplify embargo badge comment --------- Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>





Problem
The bitstream download link in the Item View gives no indication when a restricted file's embargo lifts. Fixes dataquest-dev/dspace-customers#823 (the ZCU-PUB bitstream portion; ZCU-DATA needs separate work - its file listing goes through a completely different, CLARIN-specific component that doesn't reuse
file-download-linkat all, tracked separately).Root cause
This feature already exists in vanilla DSpace and on this fork's DSpace 9.x lines (e.g.
customer/mendelu), but was never ported to the 7.x linecustomer/zcu-pubis on.History note (why this branch was rebuilt)
Earlier revisions of this PR shipped two things that turned out to be wrong and have been corrected:
dc.description.embargo/dc.date.embargoendmetadata display - wrong data source (item metadata instead of the bitstream's actual resource policy) and wrong granularity. Reverted.EmbargoBadgeComponent, copied from upstream commit5f83139(PR dataquest-dev/dspace-angular#3882, Jan 2025) - but vanilla abandoned that design; stable 9.3/main/customer/mendeluuse a unifiedAccessStatusBadgeComponentthat handles bothItemandBitstream. The standalone component also had a real bug: it readbitstream._links.accessStatus.hrefsynchronously, which throws an uncaughtTypeErroron any backend that doesn't yet expose that link (i.e. this one, until MENDELU/Fix header/nav widget shift on SSR->CSR hydration #1377 below deploys) -catchErrornever got a chance to catch it because the throw happened before the Observable pipe was constructed.This branch has been rebuilt from scratch off
customer/zcu-pubto fix both issues and to split out an unrelated accessibility fix (aria-label/role/sr-only on the lock icon, previously bundled into this same branch) into its own PR: #1390. The old Copilot review threads on this PR no longer match the current diff - the underlying commits they were reviewing were replaced by this rebuild.Change set (current, correct version)
Aligned with
customer/mendelu's existing implementation:Bitstreammodel gains anaccessStatusHAL link (same shape as the existingthumbnail/formatlinks).AccessStatusObjectgainsembargoDate.AccessStatusBadgeComponent'sobjectinput widens fromItemtoItem | Bitstream; it resolves the accessStatus link lazily viaLinkService.resolveLink(..., followLink('accessStatus', { isOptional: true }))instead of eagerly reading_links.accessStatus.href. TheisOptional: trueis required here specifically (mendelu's own code doesn't need it, because mendelu's 9.x backend already always has the link) - without it,resolveLink()throws for every bitstream on this backend today.file-download-link.component.htmlrenders<ds-themed-access-status-badge [object]="bitstream">, gated byitem.bitstream.showAccessStatuses.false(matching upstream/mendelu), nottrue- flip it only once the backend companion PR is deployed."Embargo until {{ date }}"(en) /"Embargo do {{ date }}"(cs).EmbargoBadgeComponent/ThemedEmbargoBadgeComponentand the now-unusedfindBitstreamAccessStatusFor().Backend dependency
Needs a companion REST endpoint -
GET /bitstreams/{id}/accessStatusreturning{status, embargoDate}- which doesn't exist yet oncustomer/zcu-pub. That's dataquest-dev/DSpace#1377. This frontend PR does not crash without it: with the flag at its defaultfalsethe badge never runs at all; even if the flag were flipped early, the lazy+optional link resolution fails closed (verified below).Test evidence
Includes a new regression test for the exact crash this PR fixes: constructs a real
Bitstreamwith noaccessStatusin_links, assertsngOnInitdoesn't throw, the file list renders, and no badge appears.Manual verification against a live local Docker stack running the current (pre-#1377)
customer-zcu-pubbackend image, withshowAccessStatusestemporarily forced totrue:accessStatusnetwork request was even attempted -LinkServicedetects the missing_linkskey and returnsEMPTYbefore making a call.Risk & rollback
Low risk, fails closed by construction (see above). Revert is a straight
git revert.Notes / assumptions