Skip to content

add New version action for archived submission items#130

Merged
kosarko merged 3 commits into
clarin-v7from
129-add-the-new-version-button-to-workspace-your-submissions
Apr 27, 2026
Merged

add New version action for archived submission items#130
kosarko merged 3 commits into
clarin-v7from
129-add-the-new-version-button-to-workspace-your-submissions

Conversation

@amadulhaxxani

Copy link
Copy Markdown

Problem description

On the MyDSpace Your Submissions page, archived item rows only showed the View action.
The New version action existed on item pages, but not in the submissions list, making version creation harder to discover.

Analysis

Implemented the minimal scoped solution in ds-item-actions only (archived items), without changing workspace draft actions.

  • Added New version button to archived submission actions next to View
  • Reused existing versioning flow from DsoVersioningModalService
  • Reused existing authorization check CanCreateVersion
  • Reused existing tooltip/disabled logic for draft version cases
  • Reused existing translation keys:
    • item.page.version.create
    • item.page.version.hasDraft
  • Added unit tests for:
    • visibility when authorized
    • hidden state when unauthorized
    • disabled state when draft exists
    • click handler opening create-version modal

Copilot review

  • Requested review from Copilot

 add New version action for archived submission items
Copilot AI review requested due to automatic review settings April 7, 2026 13:55
@amadulhaxxani amadulhaxxani linked an issue Apr 7, 2026 that may be closed by this pull request

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves discoverability of item versioning from the MyDSpace “Your Submissions” list by adding a New version action to archived item rows, reusing the existing versioning modal flow and authorization checks already used on item pages.

Changes:

  • Added versioning-related observables and a click handler to ItemActionsComponent to drive button visibility/disabled state/tooltip.
  • Updated the archived item actions template to render a New version button alongside View.
  • Added unit tests covering visibility, authorization, disabled state, and modal invocation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/app/shared/mydspace-actions/item/item-actions.component.ts Adds authorization + versioning state wiring and handler to open the create-version modal from the submissions list.
src/app/shared/mydspace-actions/item/item-actions.component.html Renders the New version button with tooltip/aria-label and disabled logic.
src/app/shared/mydspace-actions/item/item-actions.component.spec.ts Adds tests for button presence/absence, disabled state, and click behavior.

Comment thread src/app/shared/mydspace-actions/item/item-actions.component.spec.ts
Comment thread src/app/shared/mydspace-actions/item/item-actions.component.html
Comment thread src/app/shared/mydspace-actions/item/item-actions.component.ts
Import shareReplay and apply .pipe(shareReplay(1)) to the version draft observable in ItemActionsComponent. This shares the subscription and caches the latest emission so multiple subscribers won't trigger duplicate work or requests.
Use the disableNewVersion$ observable to compute newVersionTooltip$ (with map) and apply shareReplay to disableNewVersion$, instead of calling getVersioningTooltipMessage unconditionally. This avoids invoking the tooltip service when the button is disabled. Added map import and updated unit test to assert the tooltip is derived from the disabled state and that getVersioningTooltipMessage is not called.
@kosarko kosarko mentioned this pull request Apr 27, 2026
2 tasks
@kosarko
kosarko merged commit 3fb57f1 into clarin-v7 Apr 27, 2026
7 checks passed
@port-pr

port-pr Bot commented Apr 27, 2026

Copy link
Copy Markdown

Backport branch created but failed to create PR.
Request to create PR rejected with status 403.

Please create the PR manually:

Or via GitHub CLI:

gh pr create --repo dataquest-dev/dspace-angular --base dtq-dev --head ufal:backport-130-to-dtq-dev --title "[Port dtq-dev]  add New version action for archived submission items" --body "Port of #130 by @amadulhaxxani to `dtq-dev`."

(see action log for full error response)

milanmajchrak pushed a commit to dataquest-dev/dspace-angular that referenced this pull request Jun 3, 2026
* add New version action for archived submission items (ufal#130)

* add New version action for archived submission items

 add New version action for archived submission items

* Use shareReplay to cache version draft observable

Import shareReplay and apply .pipe(shareReplay(1)) to the version draft observable in ItemActionsComponent. This shares the subscription and caches the latest emission so multiple subscribers won't trigger duplicate work or requests.

* Derive new-version tooltip from disable state

Use the disableNewVersion$ observable to compute newVersionTooltip$ (with map) and apply shareReplay to disableNewVersion$, instead of calling getVersioningTooltipMessage unconditionally. This avoids invoking the tooltip service when the button is disabled. Added map import and updated unit test to assert the tooltip is derived from the disabled state and that getVersioningTooltipMessage is not called.

(cherry picked from commit 3fb57f1)

* Use service to get versioning tooltip

Replace the inline mapping of disableNewVersion$ to compute the tooltip with dsoVersioningModalService.getVersioningTooltipMessage(this.object, 'item.page.version.hasDraft', 'item.page.version.create'), centralizing tooltip logic in the service. Also remove the now-unused rxjs 'map' import; shareReplay usage for disableNewVersion$ is preserved.

* test: update tooltip key expectation for versioning draft state

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
milanmajchrak added a commit to dataquest-dev/dspace-angular that referenced this pull request Jul 2, 2026
* Fix incorrect translation of "withdraw" (#1191)

* used stahnout instead of odstranit

* Update confirmation text for withdrawal action

* removed two empty lines

* added empty lines as in origin

* removed spaces

---------

Co-authored-by: Kasinhou <129340513+Kasinhou@users.noreply.github.com>

* UFAL/Removed green line from the home page (#1204)

* UFAL/Added import-5 to deploy (#1206)

* UFAL/Removed unused github docker registry (#1212)

* internal/Renamed dspace-import to dspace-import-clarin in import action (#1209)

* renamed dspace-import to dspace-import-clarin

* removed unwanted changes

* UFAL/Editing similar process parameters (#1195)

* Preserve parameters when creating similar process

* Extract duplicate deep copy logic into helper

* Fix redundant parameter logic prevent empty accumulation

* UFAL/Display all versions in version history (#1213)

* Override pagesize and display all versions

* Changed hardcoded variable

---------

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* UFAL/Missing translation for new curation task (#1208)

(cherry picked from commit 5465283b9bac58d8af9513df00af90e7a0ba9baa)

* UFAL/We expect (in metadata) the path to be "licence" (#1193)

(cherry picked from commit 21521d1e16dbb7650b9dddeff5a916fdf142e200)
(cherry picked from commit c296d8771ca0ad2e82d358ac12c031a1e3488f7d)

* UFAL/Rewrite OAI links in static page HTML with rest.baseUrl (ufal/dspace-angular#90) (#1189)

* Rewrite OAI links in static page HTML with rest.baseUrl

Updated StaticPageComponent to rewrite OAI links in loaded HTML content to use the configured rest.baseUrl, ensuring correct API endpoint references. Added comprehensive tests to verify link rewriting, handling of missing baseUrl, avoidance of double slashes, and cases with no OAI links.

* Remove unused ComponentFixture import in test

Cleaned up the static-page.component.spec.ts file by removing the unused ComponentFixture import to improve code clarity.

* Fix OAI URL construction and improve test coverage

Corrects the construction of the OAI URL in StaticPageComponent to avoid double slashes by removing the extra slash in the base URL. Also updates the unit test to properly instantiate the component and check its creation.

(cherry picked from commit cb86d0778d8b5808d75b95358380a0012e79af4f)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* Update path for dspace-import in action.yml (#1216)

Use dspace-import-clarin path instead of dspace-import

* Refactor erase-db action to clean up comments

Removed unnecessary comments and improved logging for volume removal.

* Use `dspace:dspace` permissions for the assetstore - not ubuntu:ubuntu (#1222)

* Use `dspace:dspace` permissions for the assetstore - not ubuntu:ubuntu

* Change permissions as root

* First version of agents.md

* UFAL/Fix: generate correct curl download URLs using backend handle endpoint (#1215)

* fix: generate correct curl download URLs using backend handle endpoint

Updates the curl command generation to use the new backend endpoint
GET /api/core/bitstreams/handle/{prefix}/{suffix}/{filename} instead
of the non-existent /api/bitstream/{handle}/{seq}/{filename}.

Key changes:
- Uses correct backend endpoint path: /core/bitstreams/handle/{handle}/
- Removes unnecessary sequence index from URLs (uses filename only)
- Quotes the URL to prevent shell brace expansion
- For single file, uses -o with explicit filename

Fixes: #1210

* Fixed formatting of the file names

* fix: use -o with real filename to avoid percent-encoded names from curl -O

curl -O uses the URL path as the saved filename, so percent-encoded
characters (e.g. %20, %2B, %28) stay encoded in the output file.

Now generates separate 'curl -o realname url' for each file joined
with &&, ensuring files are saved with their actual names.

* fix: use curl -OJ with brace expansion for compact download command

* removed duplicates logic

* fix: use curl -o instead of -OJ to fix non-ASCII filenames on Windows

curl -J (Content-Disposition) cannot create files with non-ASCII characters
on Windows because it interprets the header bytes using the console code page.
Changed to curl -o filename url format where the shell passes the
filename directly to the OS, correctly handling Unicode on all platforms.
Also added tests for UTF-8 filenames and double-quote escaping.

* fix: use inline encodeURIComponent instead of encodeRFC3986URIComponent

encodeRFC3986URIComponent calls decodeURIComponent first, which throws
URIError on filenames containing a literal percent sign (e.g. '100% done.txt')
because '%' followed by non-hex chars is not a valid escape sequence.

Replaced with inline encodeURIComponent() + parentheses encoding directly
on the raw filename. Added test for literal percent sign in filenames.

* fix: restore brace expansion {} in curl URL with -o for filenames

curl command now uses brace expansion for compact URL:
  curl -o file1 -o file2 baseUrl{/encoded1,/encoded2}

This combines:
- {} brace expansion in the URL (compact, one URL for all files)
- -o flags with real filenames (handles UTF-8 correctly via shell)

* test: add complex filename test (diacritics, plus, hash, unmatched paren)

New FE test for 'Media (+)#9) ano' verifying correct URL encoding in brace
expansion and real filename in -o flag.

* fix: use separate -o url pairs instead of curl brace expansion

curl URL globbing ({}) does NOT support per-file -o flags. When using
  curl -o f1 -o f2 url{/a,/b}
curl maps the -o flags to URL arguments, not to globbed expansions,
resulting in 'Got more output options than URLs' and only one file saved.

Changed to separate -o + URL pairs per file:
  curl -o file1 url/file1 -o file2 url/file2

Updated all 12 test expectations to match.

* feat: show curl command in modal dialog with copy button

Replace inline command display with a centered NgbModal (size: lg) that
shows the curl command in a scrollable pre block. Includes a copy-to-clipboard
button with visual feedback (checkmark + 'Copied!' for 2s).

- Added NgbModal injection and openCommandModal()/copyCommand() methods
- Removed old isCommandLineVisible toggle and #command-div hover styles
- Added i18n keys for en, cs, de (copy/copied/close)
- Updated spec to import NgbModalModule

* Revert unnecessary changes

* Address Copilot review suggestions: accessibility, security, test fixes

- Reset canShowCurlDownload at start of generateCurlCommand()
- Add aria-labelledby to modal for screen reader accessibility
- Add .catch() to navigator.clipboard.writeText() for error handling
- Escape dollar signs and backticks in filenames for shell safety
- Fix ConfigurationDataService mock to return RemoteData-shaped object
- Add tests for canShowCurlDownload reset and shell injection protection

* fix: add fakeAsync/tick to CC license test for debounced getCcLicenseLink

---------

Co-authored-by: Paurikova2 <michaela.paurikova@dataquest.sk>

* UFAL/CURL downloads issue - fix integration test

* UFAL/Fix: preserve namespace in OAI link rewrite on static pages (#1226) (#1227)

* UFAL/Resolve duplicate HTML element IDs across pages (#1221)

* fix: resolve duplicate HTML element IDs across pages

* fix: use [attr.*] binding syntax for aria-* attributes in templates

* fix: revert dynamic form ID changes to fix e2e test failures

Reverts DsDynamicFormLayoutService and all form template changes that
were appending _N suffixes to form element IDs, breaking Cypress e2e
selectors like input#dc_title and label[for=local_hasCMDI].

Only static HTML duplicate ID fixes are retained (pagination, ds-select,
epeople-registry, etc.) along with aria-* attribute binding fixes.

* fix: address Copilot review comments - sanitize bundleName for IDs, remove invalid file-tree ID, fix conditional aria-describedby

---------

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* Refactor running rest tests in deploy

* Commented playwright for testing

* UFAL/Successfully downloaded collection file when the file is downloading (#1231)

* Force fresh bitstream fetch to avoid stale SSR 404s

* Separate bundle follow links to prevent invalid bundle/item/primaryBitstream path

* removed unwanted changes

* Removed dynamic string interpolation from URL

* Updated agents.md script

* Allows running playwright tests again

* UFAL/Resolve duplicate html IDs in submission form (#1234)

* Resolve duplicate HTML IDs in submission form.

* refactor for unique ID registry

* fix: remove unused baseId param from release in UniqueIdRegistry

* fix: propagate unique ID from container to child form controls for accessibility

* fix: recycle released suffixes in UniqueIdRegistry to preserve stable IDs across re-renders

* refactor: deduplicate UniqueIdRegistry usage, store instanceKey, and add unit tests

* Refactored the code to make this fix more simple

* Updated components to make IT pass

* refactor: replace UniqueIdRegistry with inline static counter for unique form IDs

* Add tests to verify _idState cleanup and reuse of base id after ngOnDestroy

---------

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>
Co-authored-by: milanmajchrak <milan.majchrak@dataquest.sk>

* Revert "UFAL/Refactor running rest tests in deploy" (#1238)

* UFAL/Broken matomo tracking with custom dimensions (#1237)

* fix: include dc_identifier in ViewTrackerResolverService event properties for Matomo custom dimensions

* Fixed linting error

* Upstream/EN language - fixing spaces (#1249)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* Using gh actions instead of curl-based approach to call rest tests

* Uncommented deploy and playwright

* Improve finding the id of rest tests

* Reduced the amount of code

* Trying new return output from gh workflow run

* Use directly output from gh workflow run

* Uncommented playwright and deploy - not testing

* UFAL/stabilize form array reorder/delete behavior (ufal/dspace-angular#120) (#1267)

* stabilize form array reorder/delete behavior

synchronize FormArray controls with model on reorder, dedupe patch operations by latest op+path, and fix place handling for index 0 to prevent deleted or duplicate values reappearing after save.

* Document dedupe behavior for json-patch ops

Document dedupe behavior for json-patch ops

* Simplify array index presence check

Simplify array index presence check

(cherry picked from commit b26151312c4cd61b69a7391bf945485b531c5714)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/stabilize form array reorder/delete behavior (ufal/dspace-angular#120) (#1267)

* stabilize form array reorder/delete behavior

synchronize FormArray controls with model on reorder, dedupe patch operations by latest op+path, and fix place handling for index 0 to prevent deleted or duplicate values reappearing after save.

* Document dedupe behavior for json-patch ops

Document dedupe behavior for json-patch ops

* Simplify array index presence check

Simplify array index presence check

(cherry picked from commit b26151312c4cd61b69a7391bf945485b531c5714)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/Defined logdir input (#1276)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* [Port to dtq-dev] Improve back navigation logic in ItemComponent (#1184)

* Improve back navigation logic in ItemComponent (ufal/dspace-angular#80)

* Improve back navigation logic in ItemComponent

Enhances the navigation logic to use window.history.back() when the previous URL does not match the expected route pattern. Also updates the back button visibility filter to allow empty URLs.

* Fix back button visibility logic in ItemComponent

Updated the showBackButton observable to correctly determine when the back button should be shown based on the previous route. Also added ngOnInit call in the related test to ensure proper initialization.

* Remove unused 'filter' import from item.component.ts

The 'filter' operator from 'rxjs/operators' was imported but not used in item.component.ts. This commit cleans up the import statements.

* Add session storage for previous URL in item page

Introduces generic methods in RouteService to store and retrieve URLs in session storage. ItemComponent now uses session storage to persist and retrieve the previous URL for improved back navigation, falling back to browser history if no valid URL is found.

* Add session URL helpers to RouteService stubs in tests

Extended RouteService stubs in test files to include storeUrlInSession and getUrlFromSession methods, matching the interface used by ItemComponent and related tests. This ensures test mocks are up-to-date with the service's API.

* Simplify back navigation logic in ItemComponent

Removed conditional check for previous URL and always navigate using router.navigateByUrl with storedPreviousUrl.

(cherry picked from commit 5dfb106f6f5418b88c5a37d13736810dc8f252e8)

* Treat /home as previous route and add tests

Include "/home" in ItemComponent's previousRoute regex so the back button is shown for home. Add unit tests to verify the back button appears for a home previous URL and that a home previous URL is prioritized over a session-stored URL (ensuring the session is updated and navigation uses the home URL).

(cherry picked from commit 3eb32e56a8d4248254857f0d306cbfb5653dc8d9)

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/Reset pagination on search term change (#1281)

Reset pagination on search term change

(cherry picked from commit aa495d39d915c6097fcd2517cd12300649289808)

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>

* UFAL/Updated components to display `dc.description` instead of `dc.description.abstract` (#1291)

* Updated components to display `dc.description` instead of `dc.description.abstract`

* fix(search-results): use dc.description for item description + align tests

* UFAL/Separate CLARIN license payload from sections.license (#1292)

* fix(submission): separate CLARIN license payload from sections.license

* fix(clarin-license): rename step path to /select, rename DataClarinLicense to ClarinDataLicense, and clean up comments

* Updated submission-ui test which check the clarin-license

* UFAL/123 selecting language from dropdown in submission form is broken (ufal/dspace-angular#124) (#1275)

* show ISO code on selection for language field

Ensure language autocomplete displays ISO codes consistently after selection and after form reload by handling string values in the input formatter.

* removing unnecessary comments

removing unnecessary comments

* Normalize autocomplete values and add tests

Normalize autocomplete values and add tests

(cherry picked from commit 940861d353d52b310f8f03444e20912ac9377da5)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* re-evaluate type-bind visibility when bind model loads late (ufal/dspace-angular#127) (#1274)

* re-evaluate type-bind visibility when bind model loads late

Prevent duplicate language fields after hard refresh by running an initial type-bind visibility evaluation and re-evaluating when type bind models are registered later.

* handle late type-bind model updates without duplicate setup evaluation

Improve type-bind relation subscriptions to attach listeners when bind models register late and avoid double evaluation during initialization. Add tests for late model registration, late value-change reactivity, and single setup evaluation behavior.

(cherry picked from commit e5c2c6e6233c0fbec899bd128014e4aaee27ba4c)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/Add New version button for archived submission items (#1278)

* add New version action for archived submission items (ufal/dspace-angular#130)

* add New version action for archived submission items

 add New version action for archived submission items

* Use shareReplay to cache version draft observable

Import shareReplay and apply .pipe(shareReplay(1)) to the version draft observable in ItemActionsComponent. This shares the subscription and caches the latest emission so multiple subscribers won't trigger duplicate work or requests.

* Derive new-version tooltip from disable state

Use the disableNewVersion$ observable to compute newVersionTooltip$ (with map) and apply shareReplay to disableNewVersion$, instead of calling getVersioningTooltipMessage unconditionally. This avoids invoking the tooltip service when the button is disabled. Added map import and updated unit test to assert the tooltip is derived from the disabled state and that getVersioningTooltipMessage is not called.

(cherry picked from commit 3fb57f17d9e8c05ae632aa302ea374dab115148e)

* Use service to get versioning tooltip

Replace the inline mapping of disableNewVersion$ to compute the tooltip with dsoVersioningModalService.getVersioningTooltipMessage(this.object, 'item.page.version.hasDraft', 'item.page.version.create'), centralizing tooltip logic in the service. Also remove the now-unused rxjs 'map' import; shareReplay usage for disableNewVersion$ is preserved.

* test: update tooltip key expectation for versioning draft state

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* [Port to dtq-dev] Refresh file listings on breadcrumb navigation after bitstream upload (#1280)

* Refresh file listings on breadcrumb navigation after bitstream upload (ufal/dspace-angular#112)

* Refresh file listings on breadcrumb navigation after bitstream upload

Refresh file listings on breadcrumb navigation after bitstream upload

* Move refresh logic to ngOnChanges

Move refresh logic to ngOnChanges

* Set related requests stale instead of removing cache

Set related requests stale instead of removing cache

* Use setStaleByHrefSubstring for metadatabitstreams cache

Use setStaleByHrefSubstring for metadatabitstreams cache

* Add null checks for nested RemoteData access

Add null checks for nested RemoteData access

* Use local bitstream refs and clear caches

Replace uses of this.bitstream with the local bitstream observable in edit-bitstream-page to reference the correct stream when computing image/IIIF/enabled flags.

After removing bitstreams, clear relevant caches in item-bitstreams: mark bundle bitstreams hrefs stale and invalidate metadatabitstreams search-by-handle (both encoded and raw variants) so file lists, previews and CLARIN file sections refresh correctly.

* Fix memory leak, null guard, and style issues from PR review

- PreviewSectionComponent: store and unsubscribe configService subscription
  in ngOnDestroy to prevent memory leak
- ItemBitstreamsComponent: add hasValue() null guard before bundles.forEach()
  for idiomatic style

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ondrej Kosarko <kosarko@ufal.mff.cuni.cz>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 45bacd636141527f459bcdeddfc72c633a9ce944)

* fix(bitstream): prevent combineLatest blocking when bundle is undefined

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* [Port to dtq-dev] static pages - SSR render; 404 behavior (#1241)

* Add src/static-files to build assets (ufal/dspace-angular#103)

* Add src/static-files to build assets

Update angular.json to include "src/static-files" in the build assets array so files placed there are copied into the build output. This ensures additional static resources are available at runtime without changing code paths.

* Support namespaced/SSR HTML loading; add tests

Update HtmlContentService to compose namespaced URLs, build runtime absolute URLs when running on the server (REQUEST/PLATFORM_ID), and centralize HTTP text fetching via getHtmlContent. Ensure APP_CONFIG ui.nameSpace is respected and handle absolute URLs and existing namespace prefixes. Add comprehensive unit tests (html-content.service.spec.ts) covering namespaced/default paths, locale fallback, and error handling. Adjust static-page.component.spec.ts to use a RouterMock with configurable route and add a test asserting the service is called with the route's HTML filename.

(cherry picked from commit 4eadd8b5d3d43e4c6a79644fa3822d95f5771d8d)

* ufal/dspace-angular#93 static pages and not found behavior (ufal/dspace-angular#97)

* Show 404 error for missing static pages

Updated StaticPageComponent to display an inline 404 error message when a static page is not found, matching the PageNotFoundComponent design. Added translations for the new 404 messages in both English and Czech, and set the server response status to 404 for SSR. Removed legacy error page loading logic and updated tests to mock the new ServerResponseService dependency.

* Improve static page loading and error handling

Refactors static page loading to use a new contentState property ('loading', 'found', 'not-found') for clearer UI state management and error handling. Enhances HtmlContentService with cache-busting and robust fallback logic for localized HTML content. Updates tests and template to reflect new state handling and ensures change detection is triggered after content load or error.

* Use any-cast for changeDetector spies

Replace bracket-indexing (component['changeDetector']) with (component as any).changeDetector in static-page.component.spec.ts. This updates three occurrences in the change-detection tests so TypeScript accepts spying on detectChanges without index-signature errors.

* Make cache-bust deterministic & refactor tests

Use a deterministic cache-bust parameter in HtmlContentService.appendCacheBust (avoid Date.now()) and avoid adding the param if it already exists — this keeps the behavior SSR-safe and cache-friendly. Refactor StaticPageComponent unit tests: allow setupTest to accept undefined HTML, return the mocked responseService, and simplify tests by reusing setupTest to remove duplicated test setup code.

* Add loading indicator and error logging

Show a loading spinner while static page content is fetched and add console.error logging on load failures (includes fileName and URL) to aid debugging. Also simplify HtmlContentService by removing a redundant refetch-on-empty response path that caused an extra network request and special-case 404 handling; a 200 now consistently returns the response body (or empty string). UI behavior for not-found responses remains unchanged.

* Use hourly cache-busting query param

Replace the fixed cache-bust value ('1') with a dynamic hourly value computed from Date.now()/3600000. This appends a changing query parameter so responses are refreshed hourly (reducing stale content) while keeping the existing query-separator logic intact. Removed the prior comment about a deterministic SSR-safe value.

* We don't need to manually duplicate static files

when fallback is working correctly

* Refactor HTML loading, add loading state & tests

Remove cache-busting retry logic from HtmlContentService and simplify loadHtmlContent to a single fetch attempt (returning content on 200, undefined otherwise). Update service unit tests to expect fewer HTTP calls and to not rely on cacheBust query params. Add loading-state handling in StaticPageComponent (reset stale not-found to loading, clear htmlContent at init) and adjust template to avoid rendering the 404 block while async content is pending. Enhance component tests: introduce a createDeferred helper, use fixture.whenStable/detectChanges instead of awaiting ngOnInit directly, and add tests for delayed content resolution and resetting stale not-found state.

* Trigger CI rebuild

* Use fakeAsync and tick in cc-license spec

Wrap the 'getCcLicenseLink' unit test in fakeAsync, add tick(350) and fixture.detectChanges() so the debounced observable has time to emit before the assertion. This ensures the async debounce behavior is handled reliably in the spec.

* fixed failing test

repeat the pattern that the other tests have

---------

Co-authored-by: Ondrej Kosarko <kosarko@ufal.mff.cuni.cz>
(cherry picked from commit 4a00c7daadc59decd9ff42a8fe72b85166e77f69)

* Fix broken contract link on static about page (ufal/dspace-angular#116)

* [wip] copilot proposed fix for broken contract link

There's an issue with the /static/about page. The underlying html
is @src/static-files/about.html ; when I click the link `<a
id="distribution-link" href="contract">"Distribution License
Agreement"</a>` in a browser (links) with disabled js. It works
correctly. But
  in chrome based browsers something interferes. To give a concrete
example https://dspace-dev.ufal.mff.cuni.cz/repository/static/about and
clicking the link takes me to
https://dspace-dev.ufal.mff.cuni.cz/static/contract (but when I hover
over the link I see the
  correct https://dspace-dev.ufal.mff.cuni.cz/repository/contract).

* The no_static_ links don't work without javascript

* remove the `no_static_` feature

Now all non-fragment, non-dot-relative, non-external links on
static pages use the normal app-root internal-link flow

* Intercept dot-relative static links

StaticPageComponent: simplify link handling to only intercept dot-relative links and resolve them against the static page base URL; removed fragment/external/internal redirection helpers and related code paths. Updated unit tests to reflect the new interception behavior (dot-relative under static route, nested relative resolution, and no interception for fragments or explicit app routes). Updated multiple static HTML files (English and Czech) to prefix internal anchors and links with the static/ path so they resolve correctly when served via the static route.

* Fix static page internal links

Normalize internal links across static HTML pages to use consistent paths and avoid broken relative references. Replaced occurrences of leading ./ and ../ for routes like discover and contract, and updated fragment links to use static/... (e.g. item-lifecycle, metadata, hplt-dataset-license-1.0). Affected files: src/static-files/cs/deposit.html, src/static-files/cs/faq.html, src/static-files/cs/metadata.html, src/static-files/deposit.html, src/static-files/faq.html, src/static-files/hplt-dataset-license-1.0.html, src/static-files/metadata.html.

* Up to date links

---------

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>
(cherry picked from commit 99e2e71ba4653cc63ea9062f9e49764b66fbcf72)

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* Refactor license contract page and improve collection display (ufal/clarin-dspace#114) (#1260)

* Display contract even if no collectionId given

lists all authorized collections and their licenses

* Proper cleanup

replaced the nested manual subscriptions in
license-contract-page.component.ts with a single RxJS pipeline using
tap(...), switchMap(...), and takeUntil(this.destroy$), then completed
cleanup in ngOnDestroy().

* fix non unique ids



* Guard license stream with EMPTY and stricter check

Guard license stream with EMPTY and stricter check

* Refactor license contract page and improve collection display (ufal/dspace-angular#115)

* Initial plan

* Add error/loading handling for bogus collectionId in single-collection mode



---------




---------





(cherry picked from commit 0ca142956ccfed33eae07b209ed44c03f93eace5)

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kosarko <1842385+kosarko@users.noreply.github.com>

* UFAL/Remove Clarin Matomo statistics component (#1229)

Deleted the ClarinMatomoStatisticsComponent and its related files, removed its route and module declaration, and cleaned up i18n keys associated with Matomo statistics. This streamlines the codebase by eliminating unused statistics functionality.

(cherry picked from commit 1167e194c6291de43142644615fa6c2141856e6b)
(cherry picked from commit 812c6e1594376bf0e2b22917005034eba0087e7f)

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>

* Created workflow to label new issues (#1302)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* Make Solr heap tunable via SOLR_HEAP (default 4g, no behavior change) (#1309)

* Make Solr heap tunable via SOLR_HEAP env var

Default stays 4g, so no behavior change unless SOLR_HEAP is set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add Solr params into quotes

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Skipped jcu until there will be tests (#1322)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* UFAL/fix(bitstream): restore item UUID in edit redirect (ufal/dspace-angular#154) (#1319)

* fix(bitstream): restore item UUID in edit redirect

fix(bitstream): restore item UUID in edit redirect

* fix(bitstream): improve fallback navigation and error handling

fix(bitstream): improve fallback navigation and error handling

* proper cleanup on destroy

(cherry picked from commit b4d2fac9c69b827ba2da97c5c266f9ad3fd16f12)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/[Port to dtq-dev] Add 'Add URL bitstream' feature to workspace items (#1269)

* Add 'Add URL bitstream' feature to workspace items

Introduce an "Add bitstream from URL" action for workspace items: add a conditional button and modal UI to input a file URL and optional name. Implement component support using ScriptDataService to call the 'file-downloader' script, manage processing state, show success/error notifications, and navigate to the created process detail on success. Add related unit tests (scriptDataService stub, UI visibility, invocation params, success/failure flows) and update English and Czech i18n strings.

(cherry picked from commit 65c3a5c2523be176e225405a7d7243a9df0a31eb)

* fix(accessibility): mark decorative icon as aria-hidden

---------

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>

* AI-Skills/Chore: wire private AI skills submodule (.dspace-skills) (#1324)

* UFAL/Lost license in workflow edit (ufal#144) (#1301)

* 141 lost license in workflow edit (ufal/clarin-dspace#144)

* fix(license): use route aware submission service to crash on workflow item

* Remove test-output.log

Delete the generated binary test-output.log from the repository to avoid committing test artifacts and reduce repository noise. The file has been removed from the index and will no longer be tracked.

* reverting accidental changes

reverting accidental changes

(cherry picked from commit fb97ec9f162a8cf293097b0358cc7e5337c9663c)

* Fix section-license spec for getActualSubmissionItem backport

The dtq-dev 'sendRequest should PATCH /sections/<sectionId>/select' spec
spied on getActualWorkspaceItem(), which the #144 backport replaced with
getActualSubmissionItem() (PATCHing the submission object's self link).
Update the spy and href assertion accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* UFAL/Accessibility existing issues all (ufal/dspace-angular#139) (#1300)

* Add lang attributes to description fields

Set HTML lang attributes based on metadata language for various description and metadata display elements to improve i18n and accessibility. Updated templates to bind [attr.lang] on spans, links and browse links; metadata-values now passes mdValue.language into render templates; clarin description component now emits per-entry language and value pairs; ClarinItemBoxView stores itemDescriptionLang; SidebarSearchListElement supports descriptionLang with a getDescriptionLang() hook and OrgUnit implementation returns the description language. These changes ensure displayed metadata uses the correct language attribute when available.

* Improve accessibility: add ARIA labels & i18n keys

Add ARIA attributes and move icon markup to improve screen-reader support across several components. Updated templates to include aria-label on interactive buttons (info, calendar, remove/save/delete/clear actions) and aria-hidden on decorative <i> icons. Adjusted calendar button markup to place the calendar icon inside the button element. Added corresponding i18n keys in many locale JSON5 files so the new labels are translatable (English provided and placeholder entries added across locales; some locales include TODO notes for translation). These changes are focused on accessibility and localization support.

* Improve accessibility and semantic headings

Replace several presentational heading tags with semantically appropriate heading levels (convert h4/h3/h5/h6 to h2/h3 with existing styling classes) across home, footer and item templates to improve document structure. Add aria-hidden to the hidden repository link and aria-live/aria-atomic to the pagination info for better accessibility. Update footer markup to group links into separate ULs (removing BRs) and adjust SCSS selectors to target both heading elements and .h4 class, plus spacing for stacked lists. Also add TranslateModule.forRoot() to an existing-relation-list-element spec to satisfy translations in tests.

* Update cs translations: relation group, license

Replace English strings with Czech translations in src/assets/i18n/cs.json5 for the following keys: form.relation-group.save, form.relation-group.delete, form.relation-group.clear, and submission.cc-license.field-info. Improves localization for relation-group actions and the CC license field.

* Mark decorative paperclip icon aria-hidden

Accessibility tweak: add aria-hidden="true" to the Font Awesome paperclip icon so screen readers ignore the decorative icon. Also replace the non-breaking space inside the <i> tag with a regular space between the icon and the heading text in clarin-files-section.component.html.

* Normalize metadata language to BCP 47 for HTML lang attributes

PR ufal/dspace-angular#139 binds [attr.lang] straight from the DSpace metadata language field,
which is stored as Java-style locales (en_US, cs_CZ) or the wildcard *. None
of those are valid BCP 47 / HTML lang values, so assistive technology ignores
them and the accessibility improvement is inert for the common stored values.

Add a single shared metadataLangToBcp47() helper (*/empty/nullish -> null,
"_" -> "-") and a thin dsLang pipe that delegates to it. Route all of PR #139's
lang bindings through it: the pipe in the seven templates, the helper directly
in the three .ts sites. Declare DsLangPipe in the specs that render the pipe
(NO_ERRORS_SCHEMA does not suppress missing pipes), including the shared grid
element test factory. Add unit specs for the helper and pipe plus a DOM
assertion that en_US renders as lang="en-US".

* Translate remaining English strings to Czech in cs.json5

---------



(cherry picked from commit cd1c6174bec756970e1240ecdfb1c9006cfe443a)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* [Port to dtq-dev] license management (#1299)

* 84 license labels management (ufal/dspace-angular#134)

* Add PUT and DELETE to ClarinLicenseLabel service

Replace BaseDataService with IdentifiableDataService and add PutData/DeleteData support for ClarinLicenseLabel. Wire PutDataImpl and DeleteDataImpl in the constructor, expose put(), delete() and deleteByHref() methods, and update imports (remove BaseDataService, add PutData, DeleteData, IdentifiableDataService and NoContent). Enables updating and deleting Clarin License Labels via the data service.

* Add license labels table and placeholders

Introduce a new License Labels section in the Clarin license table UI with a table, empty-state row, aria labels, and a ds-loading indicator (src/...component.html). Add CSS rule to size the actions column (src/...component.scss). In the component (src/...component.ts) add placeholder Observables (labels$, loading$) and stub methods editLabel and confirmDeleteLabel that log actions; data loading and wiring will be implemented in a follow-up task.

* Add label selection and refresh logic

Add selectable labels UI and wiring for label management:

- Template: add a radio column with aria labels, mark selected row, disable Edit/Delete unless a label is selected, and adjust colspan for empty state. Edit/Delete buttons now call no-arg handlers.
- Component: introduce selectedLabel, labels$ and loading$ BehaviorSubjects, refreshLabels() to fetch all labels via clarinLicenseLabelService.findAll (with loading/error handling), selectLabel() and isSelected() helpers, and call refreshLabels() on init and after label creation. Add ngUnsubscribe and ngOnDestroy to clean up subscriptions (takeUntil). Update scan initial state to use a defaultListState and import SortOptions.
- Tests: update spec to provide mockLicenseLabelListRD$ and spy findAll.

These changes enable selecting a license label for future edit/delete actions and keep the label list in sync with the backend.

* Add license labels management and i18n

Replace the static license-labels table with a paginated, RemoteData-driven table and translated headers; add loading state and accessible SR-only labels. Wire up edit and delete flows: open DefineLicenseLabelFormComponent for edits (convert file input, call PUT, notify and refresh) and ConfirmationModalComponent for deletes (call DELETE, notify and refresh). Introduce labelsRD$ stream, pagination options, labelsRefresh$ trigger and initializeLabelsPaginationStream to reactively fetch pages. Enhance DefineLicenseLabelFormComponent to support edit mode (prefill form, boolean extended options, aria/id fixes) and update serializer to accept booleans and legacy string values. Update and extend unit tests to cover edit/delete flows and template changes. Add new English and Czech i18n keys for the labels UI and actions.

* Use dedicated i18n key for label load errors

Introduce a specific i18n key for license label load failures and use it in the labels pagination stream. Added a labelsLoadErrorKey constant in ClarinLicenseTableComponent and replaced usages of the create-error key when handling load failures. Also added the new "clarin.license.label.load.error" translations to en.json5 and cs.json5.

* Refresh licenses after label update

Call loadAllLicenses() when a label PUT succeeds so dependent license lists are refreshed. The component now only calls refreshLabels() and loadAllLicenses() if the RemoteData indicates success. Updated unit test to spy on loadAllLicenses and assert it is called on modal submit; adjusted method comment accordingly.

* Reload licenses after label deletion

When a license label is successfully deleted, also reload the full license list to ensure UI reflects the change. Call loadAllLicenses() alongside refreshLabels() in the component, and update the spec to assert loadAllLicenses() is invoked (adding a spy and expectation).

* Hide empty table row while loading

Prevent the "no licenses" message from appearing while data is being fetched by adding a loading$ | async check to the empty-row *ngIf in clarin-license-table.component.html. The empty message now only shows when not loading and cLicenseLabels.page is empty, avoiding a flash of the empty state during load.

* Disable delete for labels in use with tooltip

Prevent deleting license labels that are referenced by any license: template changes wrap the delete button in a span that conditionally shows an ngbTooltip and uses dsBtnDisabled with a conditional click handler. Component logic now loads all licenses (paged) on init, builds a Set of in-use label IDs (inUseLabelIds) and exposes isLabelInUse to drive the UI; helper methods fetchAllLicensePages, rebuildLabelUsageSet and loadAllLicensesForUsage were added along with an allLicensesRD$ stream. Unit tests were extended to cover linked vs unlinked label behavior (disabled state, tooltip, and preventing the confirmation modal), and i18n entries for the disabled-tooltip were added for English and Czech.

* Show labels empty row only on successful load

Update clarin-license-table template to only render the labels empty-state row when the labels request has succeeded ((labelsRD$ | async)?.hasSucceeded). This prevents the empty message from appearing during failed or pending label requests. Add a unit test that simulates a failed labels request to verify the empty-state row is not shown, and import the needed createFailedRemoteDataObject helper for the test.

* Optimize license usage loading and tests

Add caching and locking around the expensive full license-usage crawl to avoid redundant requests. Introduce licenseUsageLoaded and licenseUsageLoading flags, a new ensureLicenseUsageLoaded(forceReload) helper, and a forceUsageReload parameter to loadAllLicenses so callers can explicitly invalidate the cache. Update create/edit/delete flows to force a usage reload, and set/clear the loading flags in the usage fetch success/error handlers. Add unit tests to verify that the full usage dataset is loaded only once across repeated reloads and that forcing a reload triggers a new fetch.

* F1: disable label Delete until usage crawl completes

The in-use set (inUseLabelIds) is empty until the async license usage
crawl finishes, so during that window an in-use label's Delete button
was briefly enabled. Add a usageReady$ stream that only emits true once
the crawl succeeds and gate every row's Delete button on it: all Delete
buttons stay disabled (with a "checking usage" tooltip) until usage
resolves, after which in-use labels stay disabled and unused ones enable.
On failure the buttons stay disabled so there is no unsafe window.

Frontend-only mitigation; the usage crawl itself is unchanged.

* F2: de-wrap double-nested modal in both license forms

Both define-license-form and define-license-label-form nested their own
.modal > .modal-dialog > .modal-content inside ng-bootstrap's
NgbModalWindow, which already provides those wrappers. The duplicate
chrome collapsed ngb's .modal-content and pinned the inner position:fixed
.modal to the top of the viewport instead of centering.

Remove the redundant inner wrappers so ngb owns the modal chrome, delete
the now-dead `.modal { display: ... }` scss workarounds in both forms,
fix the `modal-boy` -> `modal-body` typo in define-license-form, and open
all four form modals with { centered: true } so the dialog is centered.

* F3: surface newly created label without manual paging

The labels endpoint lists labels in ascending insertion order and ignores
the sort param, so a freshly created label received the highest id and
landed on the last pagination page, invisible to the admin after Save.
After a successful create, jump the labels table to the last page (derived
from the current total plus the one just added) so the new row is shown
immediately.

* F4: allow clearing a label icon on edit and preview the current icon

The label edit form could keep or replace an icon but never remove one,
and gave no indication of the current icon. Add a "Remove current icon"
checkbox and a small preview (reusing secureImageData) shown in edit mode
when the label has an icon. When clear is requested and no new file is
chosen, the table sends an empty icon array through the existing PUT path
to remove it; selecting a new file still takes precedence.

* F5: center the delete-confirmation modal and complete cs.json5 label parity

Pass { centered: true } when opening ConfirmationModalComponent from
confirmDeleteLabel() so the delete-confirmation dialog is vertically
centered like the four license/label form dialogs, removing the
top-aligned visual inconsistency.

Also add the three Czech translations that were missing from cs.json5
(table.delete.checking-tooltip, edit.icon.preview, edit.icon.clear) so
the en/cs label key sets are back in parity.

* test: expect centered option in delete-confirmation modal spec

The F5 change passes { centered: true } to modalService.open; update the
assertion to match so the label delete flow spec passes.

---------

Co-authored-by: kosarko <ko_ok@centrum.cz>
(cherry picked from commit 87cb7a7f007a0190894f4e4a772489a92a7c976b)

* Disable delete for in-use licenses, add tooltip (ufal/dspace-angular#137)

* Disable delete for in-use licenses, add tooltip

Prevent deleting licenses that are attached to bitstreams by disabling the Delete button and showing a tooltip explaining why. Adds UI markup (ngbTooltip wrapper, aria-label, dsBtnDisabled and guarded click), a new isSelectedLicenseInUse() helper and a check in deleteLicense(). Adds unit tests covering disabled/enabled states and click behavior, and provides i18n strings for English and Czech for the disabled-tooltip.

* Use hasNoValue for license id check

Replace isNull with hasNoValue when validating selectedLicense.id in deleteLicense to correctly handle undefined/null values. Also add hasNoValue to the imports from shared/empty.util.

* Use NgbTooltip instance in delete button spec

Update clarin-license-table component spec to import NgbTooltip and retrieve the tooltip instance from the delete wrapper's injector. Replace the previous assertion that inspected the DOM's ng-reflect-ngb-tooltip attribute with an assertion on deleteTooltip.ngbTooltip to verify the expected translation key. This makes the test more robust by avoiding reliance on Angular's rendered reflection attribute.

(cherry picked from commit 42c4fccba0bb87d9f2f05dd245745b194d599dff)

* Fix license label issues flagged in code review

- Change label icon fallback from selectedLabel.icon to undefined
  to avoid sending a base64 string when the icon is unchanged
- Correct 0‑based page parameter to 1‑based in fetchAllLicensePages
- Replace hard‑coded aria-label on modal close button with a
  translatable key for accessibility

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* [Port to dtq-dev] Add search icon to community and collection (ufal/dspace-angular#109) (#1326)

* add search icon to community and collection

add search icon to community and collection, click should navigate to community/collection pre-selected advance search page
add tests to confirm the new navigation

* Remove redundant wrapper div from collection/community page

Remove redundant wrapper div from collection/community page

(cherry picked from commit 385b33dc69d9569beb4529fe3863c4147b621a47)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* test: fix flaky DSDynamicTypeBindRelationService spec (double spyOn) (#1338)

* test: fix flaky DSDynamicTypeBindRelationService spec (double spyOn)

The FormBuilderService mock (getMockFormBuilderService) is a jasmine.createSpyObj,
so getTypeBindModel / getTypeBindModelUpdates are already spies. Several specs then
call spyOn(...) on them again, which Jasmine rejects with
"<spyOn> : getTypeBindModel has already been spied upon". Under Karma/Jasmine's
randomized spec order this surfaced intermittently, so the same commit passed some
CI runs and failed others.

Reconfigure the existing spies via (spy as jasmine.Spy).and.returnValue/callFake(...)
instead of re-spying them (the standard idiom for a createSpyObj mock). The spy
behaviour per test is identical, so all assertions are unchanged; the illegal
double-spyOn is removed, making "already been spied upon" impossible in any order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: await TestBed setup in DSDynamicTypeBindRelationService spec

Per review: the first beforeEach did
`TestBed.configureTestingModule({...}).compileComponents().then();` — the promise
was never returned/awaited, so nothing guaranteed the module (and its fresh
getMockFormBuilderService() spy mock) was fully set up before each spec, which is
the kind of async setup slop that makes the same commit pass some CI runs and fail
others. Return the promise so Jasmine awaits it and every spec starts from a fresh,
fully-configured module. Complements the spy-reconfiguration fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: reword beforeEach comment (address Copilot review)

Replace the colloquial "async setup slop" wording with a clearer description
("letting asynchronous module setup leak between specs"). Comment-only change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* UFAL/Locale names for SimpleRORAuthority (ufal/dspace-angular#135) (#1337)

* Locale names for SimpleRORAuthority

(cherry picked from commit 1d367aa4692e4226da2e6cdbafe26ce08dcb43b5)

Co-authored-by: Milan Kuchtiak <kuchtiak@ufal.mff.cuni.cz>

* UFAL/fix(submission): keep dc.type dropdown display after change (ufal/dspace-angular#157) (#1336)

* fix(submission): keep dc.type dropdown display after change

(ufal/clarin-dspace#1377 -> ufal/dspace-angular#156)

The "type of resource" scrollable dropdown blanked out after changing its
value when the backend was slow: the reloaded model value came back with an
empty display but populated value/authority, so currentValue rendered ''.
Fall back to the underlying value when display is empty, matching the
existing inputFormatter convention.

Add a fakeAsync regression test that drives an empty-display value through
group.valueChanges (the real reload entry point) and asserts the rendered
input keeps its text, reproducing the original blank-after-latency bug.

* test(submission): make empty-display init test deterministic

Address Copilot PR review: the init-path test previously relied on
isNotEmpty(new VocabularyEntry()) being false so getInitValueFromModel would
fall back to the raw model.value. That couples the test to VocabularyEntry
having no initialized fields. Stub getInitValueFromModel directly to emit an
empty-display value (built bypassing the FFMVO constructor, which would
otherwise fill display via `display || value`), so the test deterministically
exercises the setCurrentValue fallback it is meant to guard.

* test(type-bind): stop re-spying mock methods that are already spies

The DSDynamicTypeBindRelationService spec called spyOn() on
formBuilderService.getTypeBindModel / getTypeBindModelUpdates, but the mock
(getMockFormBuilderService) already creates those as jasmine spies via
createSpyObj. Because src/test.ts runs with teardown.destroyAfterEach:false
and karma randomizes spec order, certain orderings hit the second spy
installation and threw "getTypeBindModel has already been spied upon",
failing 6 tests non-deterministically (green on clarin-v7, red here only due
to a different random ordering). Reconfigure the existing spies with .and.*
instead of re-spying, which is order-independent.

(cherry picked from commit 8580e90ae91edce451258ffdf01568ee8bbb0af0)

* Revert unwanted change

---------

Co-authored-by: Paurikova2 <107862249+Paurikova2@users.noreply.github.com>
Co-authored-by: Kasinhou <129340513+Kasinhou@users.noreply.github.com>
Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>
Co-authored-by: Ondřej Košarko <ko_ok@centrum.cz>
Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: Jozef Misutka <332350+vidiecan@users.noreply.github.com>
Co-authored-by: Paurikova2 <michaela.paurikova@dataquest.sk>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kosarko <1842385+kosarko@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: jurinecko <juraj.roka@dataquest.sk>
Co-authored-by: Milan Kuchtiak <kuchtiak@ufal.mff.cuni.cz>
milanmajchrak added a commit to dataquest-dev/dspace-angular that referenced this pull request Jul 14, 2026
* Fix incorrect translation of "withdraw" (#1191)

* used stahnout instead of odstranit

* Update confirmation text for withdrawal action

* removed two empty lines

* added empty lines as in origin

* removed spaces

---------

Co-authored-by: Kasinhou <129340513+Kasinhou@users.noreply.github.com>

* UFAL/Removed green line from the home page (#1204)

* UFAL/Added import-5 to deploy (#1206)

* UFAL/Removed unused github docker registry (#1212)

* internal/Renamed dspace-import to dspace-import-clarin in import action (#1209)

* renamed dspace-import to dspace-import-clarin

* removed unwanted changes

* UFAL/Editing similar process parameters (#1195)

* Preserve parameters when creating similar process

* Extract duplicate deep copy logic into helper

* Fix redundant parameter logic prevent empty accumulation

* UFAL/Display all versions in version history (#1213)

* Override pagesize and display all versions

* Changed hardcoded variable

---------

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* UFAL/Missing translation for new curation task (#1208)

(cherry picked from commit 5465283b9bac58d8af9513df00af90e7a0ba9baa)

* UFAL/We expect (in metadata) the path to be "licence" (#1193)

(cherry picked from commit 21521d1e16dbb7650b9dddeff5a916fdf142e200)
(cherry picked from commit c296d8771ca0ad2e82d358ac12c031a1e3488f7d)

* UFAL/Rewrite OAI links in static page HTML with rest.baseUrl (ufal/dspace-angular#90) (#1189)

* Rewrite OAI links in static page HTML with rest.baseUrl

Updated StaticPageComponent to rewrite OAI links in loaded HTML content to use the configured rest.baseUrl, ensuring correct API endpoint references. Added comprehensive tests to verify link rewriting, handling of missing baseUrl, avoidance of double slashes, and cases with no OAI links.

* Remove unused ComponentFixture import in test

Cleaned up the static-page.component.spec.ts file by removing the unused ComponentFixture import to improve code clarity.

* Fix OAI URL construction and improve test coverage

Corrects the construction of the OAI URL in StaticPageComponent to avoid double slashes by removing the extra slash in the base URL. Also updates the unit test to properly instantiate the component and check its creation.

(cherry picked from commit cb86d0778d8b5808d75b95358380a0012e79af4f)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* Update path for dspace-import in action.yml (#1216)

Use dspace-import-clarin path instead of dspace-import

* Refactor erase-db action to clean up comments

Removed unnecessary comments and improved logging for volume removal.

* Use `dspace:dspace` permissions for the assetstore - not ubuntu:ubuntu (#1222)

* Use `dspace:dspace` permissions for the assetstore - not ubuntu:ubuntu

* Change permissions as root

* First version of agents.md

* UFAL/Fix: generate correct curl download URLs using backend handle endpoint (#1215)

* fix: generate correct curl download URLs using backend handle endpoint

Updates the curl command generation to use the new backend endpoint
GET /api/core/bitstreams/handle/{prefix}/{suffix}/{filename} instead
of the non-existent /api/bitstream/{handle}/{seq}/{filename}.

Key changes:
- Uses correct backend endpoint path: /core/bitstreams/handle/{handle}/
- Removes unnecessary sequence index from URLs (uses filename only)
- Quotes the URL to prevent shell brace expansion
- For single file, uses -o with explicit filename

Fixes: #1210

* Fixed formatting of the file names

* fix: use -o with real filename to avoid percent-encoded names from curl -O

curl -O uses the URL path as the saved filename, so percent-encoded
characters (e.g. %20, %2B, %28) stay encoded in the output file.

Now generates separate 'curl -o realname url' for each file joined
with &&, ensuring files are saved with their actual names.

* fix: use curl -OJ with brace expansion for compact download command

* removed duplicates logic

* fix: use curl -o instead of -OJ to fix non-ASCII filenames on Windows

curl -J (Content-Disposition) cannot create files with non-ASCII characters
on Windows because it interprets the header bytes using the console code page.
Changed to curl -o filename url format where the shell passes the
filename directly to the OS, correctly handling Unicode on all platforms.
Also added tests for UTF-8 filenames and double-quote escaping.

* fix: use inline encodeURIComponent instead of encodeRFC3986URIComponent

encodeRFC3986URIComponent calls decodeURIComponent first, which throws
URIError on filenames containing a literal percent sign (e.g. '100% done.txt')
because '%' followed by non-hex chars is not a valid escape sequence.

Replaced with inline encodeURIComponent() + parentheses encoding directly
on the raw filename. Added test for literal percent sign in filenames.

* fix: restore brace expansion {} in curl URL with -o for filenames

curl command now uses brace expansion for compact URL:
  curl -o file1 -o file2 baseUrl{/encoded1,/encoded2}

This combines:
- {} brace expansion in the URL (compact, one URL for all files)
- -o flags with real filenames (handles UTF-8 correctly via shell)

* test: add complex filename test (diacritics, plus, hash, unmatched paren)

New FE test for 'Media (+)#9) ano' verifying correct URL encoding in brace
expansion and real filename in -o flag.

* fix: use separate -o url pairs instead of curl brace expansion

curl URL globbing ({}) does NOT support per-file -o flags. When using
  curl -o f1 -o f2 url{/a,/b}
curl maps the -o flags to URL arguments, not to globbed expansions,
resulting in 'Got more output options than URLs' and only one file saved.

Changed to separate -o + URL pairs per file:
  curl -o file1 url/file1 -o file2 url/file2

Updated all 12 test expectations to match.

* feat: show curl command in modal dialog with copy button

Replace inline command display with a centered NgbModal (size: lg) that
shows the curl command in a scrollable pre block. Includes a copy-to-clipboard
button with visual feedback (checkmark + 'Copied!' for 2s).

- Added NgbModal injection and openCommandModal()/copyCommand() methods
- Removed old isCommandLineVisible toggle and #command-div hover styles
- Added i18n keys for en, cs, de (copy/copied/close)
- Updated spec to import NgbModalModule

* Revert unnecessary changes

* Address Copilot review suggestions: accessibility, security, test fixes

- Reset canShowCurlDownload at start of generateCurlCommand()
- Add aria-labelledby to modal for screen reader accessibility
- Add .catch() to navigator.clipboard.writeText() for error handling
- Escape dollar signs and backticks in filenames for shell safety
- Fix ConfigurationDataService mock to return RemoteData-shaped object
- Add tests for canShowCurlDownload reset and shell injection protection

* fix: add fakeAsync/tick to CC license test for debounced getCcLicenseLink

---------

Co-authored-by: Paurikova2 <michaela.paurikova@dataquest.sk>

* UFAL/CURL downloads issue - fix integration test

* UFAL/Fix: preserve namespace in OAI link rewrite on static pages (#1226) (#1227)

* UFAL/Resolve duplicate HTML element IDs across pages (#1221)

* fix: resolve duplicate HTML element IDs across pages

* fix: use [attr.*] binding syntax for aria-* attributes in templates

* fix: revert dynamic form ID changes to fix e2e test failures

Reverts DsDynamicFormLayoutService and all form template changes that
were appending _N suffixes to form element IDs, breaking Cypress e2e
selectors like input#dc_title and label[for=local_hasCMDI].

Only static HTML duplicate ID fixes are retained (pagination, ds-select,
epeople-registry, etc.) along with aria-* attribute binding fixes.

* fix: address Copilot review comments - sanitize bundleName for IDs, remove invalid file-tree ID, fix conditional aria-describedby

---------

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* Refactor running rest tests in deploy

* Commented playwright for testing

* UFAL/Successfully downloaded collection file when the file is downloading (#1231)

* Force fresh bitstream fetch to avoid stale SSR 404s

* Separate bundle follow links to prevent invalid bundle/item/primaryBitstream path

* removed unwanted changes

* Removed dynamic string interpolation from URL

* Updated agents.md script

* Allows running playwright tests again

* UFAL/Resolve duplicate html IDs in submission form (#1234)

* Resolve duplicate HTML IDs in submission form.

* refactor for unique ID registry

* fix: remove unused baseId param from release in UniqueIdRegistry

* fix: propagate unique ID from container to child form controls for accessibility

* fix: recycle released suffixes in UniqueIdRegistry to preserve stable IDs across re-renders

* refactor: deduplicate UniqueIdRegistry usage, store instanceKey, and add unit tests

* Refactored the code to make this fix more simple

* Updated components to make IT pass

* refactor: replace UniqueIdRegistry with inline static counter for unique form IDs

* Add tests to verify _idState cleanup and reuse of base id after ngOnDestroy

---------

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>
Co-authored-by: milanmajchrak <milan.majchrak@dataquest.sk>

* Revert "UFAL/Refactor running rest tests in deploy" (#1238)

* UFAL/Broken matomo tracking with custom dimensions (#1237)

* fix: include dc_identifier in ViewTrackerResolverService event properties for Matomo custom dimensions

* Fixed linting error

* Upstream/EN language - fixing spaces (#1249)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* Using gh actions instead of curl-based approach to call rest tests

* Uncommented deploy and playwright

* Improve finding the id of rest tests

* Reduced the amount of code

* Trying new return output from gh workflow run

* Use directly output from gh workflow run

* Uncommented playwright and deploy - not testing

* UFAL/stabilize form array reorder/delete behavior (ufal/dspace-angular#120) (#1267)

* stabilize form array reorder/delete behavior

synchronize FormArray controls with model on reorder, dedupe patch operations by latest op+path, and fix place handling for index 0 to prevent deleted or duplicate values reappearing after save.

* Document dedupe behavior for json-patch ops

Document dedupe behavior for json-patch ops

* Simplify array index presence check

Simplify array index presence check

(cherry picked from commit b26151312c4cd61b69a7391bf945485b531c5714)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/stabilize form array reorder/delete behavior (ufal/dspace-angular#120) (#1267)

* stabilize form array reorder/delete behavior

synchronize FormArray controls with model on reorder, dedupe patch operations by latest op+path, and fix place handling for index 0 to prevent deleted or duplicate values reappearing after save.

* Document dedupe behavior for json-patch ops

Document dedupe behavior for json-patch ops

* Simplify array index presence check

Simplify array index presence check

(cherry picked from commit b26151312c4cd61b69a7391bf945485b531c5714)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/Defined logdir input (#1276)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* [Port to dtq-dev] Improve back navigation logic in ItemComponent (#1184)

* Improve back navigation logic in ItemComponent (ufal/dspace-angular#80)

* Improve back navigation logic in ItemComponent

Enhances the navigation logic to use window.history.back() when the previous URL does not match the expected route pattern. Also updates the back button visibility filter to allow empty URLs.

* Fix back button visibility logic in ItemComponent

Updated the showBackButton observable to correctly determine when the back button should be shown based on the previous route. Also added ngOnInit call in the related test to ensure proper initialization.

* Remove unused 'filter' import from item.component.ts

The 'filter' operator from 'rxjs/operators' was imported but not used in item.component.ts. This commit cleans up the import statements.

* Add session storage for previous URL in item page

Introduces generic methods in RouteService to store and retrieve URLs in session storage. ItemComponent now uses session storage to persist and retrieve the previous URL for improved back navigation, falling back to browser history if no valid URL is found.

* Add session URL helpers to RouteService stubs in tests

Extended RouteService stubs in test files to include storeUrlInSession and getUrlFromSession methods, matching the interface used by ItemComponent and related tests. This ensures test mocks are up-to-date with the service's API.

* Simplify back navigation logic in ItemComponent

Removed conditional check for previous URL and always navigate using router.navigateByUrl with storedPreviousUrl.

(cherry picked from commit 5dfb106f6f5418b88c5a37d13736810dc8f252e8)

* Treat /home as previous route and add tests

Include "/home" in ItemComponent's previousRoute regex so the back button is shown for home. Add unit tests to verify the back button appears for a home previous URL and that a home previous URL is prioritized over a session-stored URL (ensuring the session is updated and navigation uses the home URL).

(cherry picked from commit 3eb32e56a8d4248254857f0d306cbfb5653dc8d9)

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/Reset pagination on search term change (#1281)

Reset pagination on search term change

(cherry picked from commit aa495d39d915c6097fcd2517cd12300649289808)

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>

* UFAL/Updated components to display `dc.description` instead of `dc.description.abstract` (#1291)

* Updated components to display `dc.description` instead of `dc.description.abstract`

* fix(search-results): use dc.description for item description + align tests

* UFAL/Separate CLARIN license payload from sections.license (#1292)

* fix(submission): separate CLARIN license payload from sections.license

* fix(clarin-license): rename step path to /select, rename DataClarinLicense to ClarinDataLicense, and clean up comments

* Updated submission-ui test which check the clarin-license

* UFAL/123 selecting language from dropdown in submission form is broken (ufal/dspace-angular#124) (#1275)

* show ISO code on selection for language field

Ensure language autocomplete displays ISO codes consistently after selection and after form reload by handling string values in the input formatter.

* removing unnecessary comments

removing unnecessary comments

* Normalize autocomplete values and add tests

Normalize autocomplete values and add tests

(cherry picked from commit 940861d353d52b310f8f03444e20912ac9377da5)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* re-evaluate type-bind visibility when bind model loads late (ufal/dspace-angular#127) (#1274)

* re-evaluate type-bind visibility when bind model loads late

Prevent duplicate language fields after hard refresh by running an initial type-bind visibility evaluation and re-evaluating when type bind models are registered later.

* handle late type-bind model updates without duplicate setup evaluation

Improve type-bind relation subscriptions to attach listeners when bind models register late and avoid double evaluation during initialization. Add tests for late model registration, late value-change reactivity, and single setup evaluation behavior.

(cherry picked from commit e5c2c6e6233c0fbec899bd128014e4aaee27ba4c)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/Add New version button for archived submission items (#1278)

* add New version action for archived submission items (ufal/dspace-angular#130)

* add New version action for archived submission items

 add New version action for archived submission items

* Use shareReplay to cache version draft observable

Import shareReplay and apply .pipe(shareReplay(1)) to the version draft observable in ItemActionsComponent. This shares the subscription and caches the latest emission so multiple subscribers won't trigger duplicate work or requests.

* Derive new-version tooltip from disable state

Use the disableNewVersion$ observable to compute newVersionTooltip$ (with map) and apply shareReplay to disableNewVersion$, instead of calling getVersioningTooltipMessage unconditionally. This avoids invoking the tooltip service when the button is disabled. Added map import and updated unit test to assert the tooltip is derived from the disabled state and that getVersioningTooltipMessage is not called.

(cherry picked from commit 3fb57f17d9e8c05ae632aa302ea374dab115148e)

* Use service to get versioning tooltip

Replace the inline mapping of disableNewVersion$ to compute the tooltip with dsoVersioningModalService.getVersioningTooltipMessage(this.object, 'item.page.version.hasDraft', 'item.page.version.create'), centralizing tooltip logic in the service. Also remove the now-unused rxjs 'map' import; shareReplay usage for disableNewVersion$ is preserved.

* test: update tooltip key expectation for versioning draft state

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* [Port to dtq-dev] Refresh file listings on breadcrumb navigation after bitstream upload (#1280)

* Refresh file listings on breadcrumb navigation after bitstream upload (ufal/dspace-angular#112)

* Refresh file listings on breadcrumb navigation after bitstream upload

Refresh file listings on breadcrumb navigation after bitstream upload

* Move refresh logic to ngOnChanges

Move refresh logic to ngOnChanges

* Set related requests stale instead of removing cache

Set related requests stale instead of removing cache

* Use setStaleByHrefSubstring for metadatabitstreams cache

Use setStaleByHrefSubstring for metadatabitstreams cache

* Add null checks for nested RemoteData access

Add null checks for nested RemoteData access

* Use local bitstream refs and clear caches

Replace uses of this.bitstream with the local bitstream observable in edit-bitstream-page to reference the correct stream when computing image/IIIF/enabled flags.

After removing bitstreams, clear relevant caches in item-bitstreams: mark bundle bitstreams hrefs stale and invalidate metadatabitstreams search-by-handle (both encoded and raw variants) so file lists, previews and CLARIN file sections refresh correctly.

* Fix memory leak, null guard, and style issues from PR review

- PreviewSectionComponent: store and unsubscribe configService subscription
  in ngOnDestroy to prevent memory leak
- ItemBitstreamsComponent: add hasValue() null guard before bundles.forEach()
  for idiomatic style

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ondrej Kosarko <kosarko@ufal.mff.cuni.cz>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 45bacd636141527f459bcdeddfc72c633a9ce944)

* fix(bitstream): prevent combineLatest blocking when bundle is undefined

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* [Port to dtq-dev] static pages - SSR render; 404 behavior (#1241)

* Add src/static-files to build assets (ufal/dspace-angular#103)

* Add src/static-files to build assets

Update angular.json to include "src/static-files" in the build assets array so files placed there are copied into the build output. This ensures additional static resources are available at runtime without changing code paths.

* Support namespaced/SSR HTML loading; add tests

Update HtmlContentService to compose namespaced URLs, build runtime absolute URLs when running on the server (REQUEST/PLATFORM_ID), and centralize HTTP text fetching via getHtmlContent. Ensure APP_CONFIG ui.nameSpace is respected and handle absolute URLs and existing namespace prefixes. Add comprehensive unit tests (html-content.service.spec.ts) covering namespaced/default paths, locale fallback, and error handling. Adjust static-page.component.spec.ts to use a RouterMock with configurable route and add a test asserting the service is called with the route's HTML filename.

(cherry picked from commit 4eadd8b5d3d43e4c6a79644fa3822d95f5771d8d)

* ufal/dspace-angular#93 static pages and not found behavior (ufal/dspace-angular#97)

* Show 404 error for missing static pages

Updated StaticPageComponent to display an inline 404 error message when a static page is not found, matching the PageNotFoundComponent design. Added translations for the new 404 messages in both English and Czech, and set the server response status to 404 for SSR. Removed legacy error page loading logic and updated tests to mock the new ServerResponseService dependency.

* Improve static page loading and error handling

Refactors static page loading to use a new contentState property ('loading', 'found', 'not-found') for clearer UI state management and error handling. Enhances HtmlContentService with cache-busting and robust fallback logic for localized HTML content. Updates tests and template to reflect new state handling and ensures change detection is triggered after content load or error.

* Use any-cast for changeDetector spies

Replace bracket-indexing (component['changeDetector']) with (component as any).changeDetector in static-page.component.spec.ts. This updates three occurrences in the change-detection tests so TypeScript accepts spying on detectChanges without index-signature errors.

* Make cache-bust deterministic & refactor tests

Use a deterministic cache-bust parameter in HtmlContentService.appendCacheBust (avoid Date.now()) and avoid adding the param if it already exists — this keeps the behavior SSR-safe and cache-friendly. Refactor StaticPageComponent unit tests: allow setupTest to accept undefined HTML, return the mocked responseService, and simplify tests by reusing setupTest to remove duplicated test setup code.

* Add loading indicator and error logging

Show a loading spinner while static page content is fetched and add console.error logging on load failures (includes fileName and URL) to aid debugging. Also simplify HtmlContentService by removing a redundant refetch-on-empty response path that caused an extra network request and special-case 404 handling; a 200 now consistently returns the response body (or empty string). UI behavior for not-found responses remains unchanged.

* Use hourly cache-busting query param

Replace the fixed cache-bust value ('1') with a dynamic hourly value computed from Date.now()/3600000. This appends a changing query parameter so responses are refreshed hourly (reducing stale content) while keeping the existing query-separator logic intact. Removed the prior comment about a deterministic SSR-safe value.

* We don't need to manually duplicate static files

when fallback is working correctly

* Refactor HTML loading, add loading state & tests

Remove cache-busting retry logic from HtmlContentService and simplify loadHtmlContent to a single fetch attempt (returning content on 200, undefined otherwise). Update service unit tests to expect fewer HTTP calls and to not rely on cacheBust query params. Add loading-state handling in StaticPageComponent (reset stale not-found to loading, clear htmlContent at init) and adjust template to avoid rendering the 404 block while async content is pending. Enhance component tests: introduce a createDeferred helper, use fixture.whenStable/detectChanges instead of awaiting ngOnInit directly, and add tests for delayed content resolution and resetting stale not-found state.

* Trigger CI rebuild

* Use fakeAsync and tick in cc-license spec

Wrap the 'getCcLicenseLink' unit test in fakeAsync, add tick(350) and fixture.detectChanges() so the debounced observable has time to emit before the assertion. This ensures the async debounce behavior is handled reliably in the spec.

* fixed failing test

repeat the pattern that the other tests have

---------

Co-authored-by: Ondrej Kosarko <kosarko@ufal.mff.cuni.cz>
(cherry picked from commit 4a00c7daadc59decd9ff42a8fe72b85166e77f69)

* Fix broken contract link on static about page (ufal/dspace-angular#116)

* [wip] copilot proposed fix for broken contract link

There's an issue with the /static/about page. The underlying html
is @src/static-files/about.html ; when I click the link `<a
id="distribution-link" href="contract">"Distribution License
Agreement"</a>` in a browser (links) with disabled js. It works
correctly. But
  in chrome based browsers something interferes. To give a concrete
example https://dspace-dev.ufal.mff.cuni.cz/repository/static/about and
clicking the link takes me to
https://dspace-dev.ufal.mff.cuni.cz/static/contract (but when I hover
over the link I see the
  correct https://dspace-dev.ufal.mff.cuni.cz/repository/contract).

* The no_static_ links don't work without javascript

* remove the `no_static_` feature

Now all non-fragment, non-dot-relative, non-external links on
static pages use the normal app-root internal-link flow

* Intercept dot-relative static links

StaticPageComponent: simplify link handling to only intercept dot-relative links and resolve them against the static page base URL; removed fragment/external/internal redirection helpers and related code paths. Updated unit tests to reflect the new interception behavior (dot-relative under static route, nested relative resolution, and no interception for fragments or explicit app routes). Updated multiple static HTML files (English and Czech) to prefix internal anchors and links with the static/ path so they resolve correctly when served via the static route.

* Fix static page internal links

Normalize internal links across static HTML pages to use consistent paths and avoid broken relative references. Replaced occurrences of leading ./ and ../ for routes like discover and contract, and updated fragment links to use static/... (e.g. item-lifecycle, metadata, hplt-dataset-license-1.0). Affected files: src/static-files/cs/deposit.html, src/static-files/cs/faq.html, src/static-files/cs/metadata.html, src/static-files/deposit.html, src/static-files/faq.html, src/static-files/hplt-dataset-license-1.0.html, src/static-files/metadata.html.

* Up to date links

---------

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>
(cherry picked from commit 99e2e71ba4653cc63ea9062f9e49764b66fbcf72)

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* Refactor license contract page and improve collection display (ufal/clarin-dspace#114) (#1260)

* Display contract even if no collectionId given

lists all authorized collections and their licenses

* Proper cleanup

replaced the nested manual subscriptions in
license-contract-page.component.ts with a single RxJS pipeline using
tap(...), switchMap(...), and takeUntil(this.destroy$), then completed
cleanup in ngOnDestroy().

* fix non unique ids



* Guard license stream with EMPTY and stricter check

Guard license stream with EMPTY and stricter check

* Refactor license contract page and improve collection display (ufal/dspace-angular#115)

* Initial plan

* Add error/loading handling for bogus collectionId in single-collection mode



---------




---------





(cherry picked from commit 0ca142956ccfed33eae07b209ed44c03f93eace5)

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kosarko <1842385+kosarko@users.noreply.github.com>

* UFAL/Remove Clarin Matomo statistics component (#1229)

Deleted the ClarinMatomoStatisticsComponent and its related files, removed its route and module declaration, and cleaned up i18n keys associated with Matomo statistics. This streamlines the codebase by eliminating unused statistics functionality.

(cherry picked from commit 1167e194c6291de43142644615fa6c2141856e6b)
(cherry picked from commit 812c6e1594376bf0e2b22917005034eba0087e7f)

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>

* Created workflow to label new issues (#1302)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* Make Solr heap tunable via SOLR_HEAP (default 4g, no behavior change) (#1309)

* Make Solr heap tunable via SOLR_HEAP env var

Default stays 4g, so no behavior change unless SOLR_HEAP is set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add Solr params into quotes

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Skipped jcu until there will be tests (#1322)

Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>

* UFAL/fix(bitstream): restore item UUID in edit redirect (ufal/dspace-angular#154) (#1319)

* fix(bitstream): restore item UUID in edit redirect

fix(bitstream): restore item UUID in edit redirect

* fix(bitstream): improve fallback navigation and error handling

fix(bitstream): improve fallback navigation and error handling

* proper cleanup on destroy

(cherry picked from commit b4d2fac9c69b827ba2da97c5c266f9ad3fd16f12)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/[Port to dtq-dev] Add 'Add URL bitstream' feature to workspace items (#1269)

* Add 'Add URL bitstream' feature to workspace items

Introduce an "Add bitstream from URL" action for workspace items: add a conditional button and modal UI to input a file URL and optional name. Implement component support using ScriptDataService to call the 'file-downloader' script, manage processing state, show success/error notifications, and navigate to the created process detail on success. Add related unit tests (scriptDataService stub, UI visibility, invocation params, success/failure flows) and update English and Czech i18n strings.

(cherry picked from commit 65c3a5c2523be176e225405a7d7243a9df0a31eb)

* fix(accessibility): mark decorative icon as aria-hidden

---------

Co-authored-by: amadulhaxxani <hassan@ufal.mff.cuni.cz>

* AI-Skills/Chore: wire private AI skills submodule (.dspace-skills) (#1324)

* UFAL/Lost license in workflow edit (ufal#144) (#1301)

* 141 lost license in workflow edit (ufal/clarin-dspace#144)

* fix(license): use route aware submission service to crash on workflow item

* Remove test-output.log

Delete the generated binary test-output.log from the repository to avoid committing test artifacts and reduce repository noise. The file has been removed from the index and will no longer be tracked.

* reverting accidental changes

reverting accidental changes

(cherry picked from commit fb97ec9f162a8cf293097b0358cc7e5337c9663c)

* Fix section-license spec for getActualSubmissionItem backport

The dtq-dev 'sendRequest should PATCH /sections/<sectionId>/select' spec
spied on getActualWorkspaceItem(), which the #144 backport replaced with
getActualSubmissionItem() (PATCHing the submission object's self link).
Update the spy and href assertion accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* UFAL/Accessibility existing issues all (ufal/dspace-angular#139) (#1300)

* Add lang attributes to description fields

Set HTML lang attributes based on metadata language for various description and metadata display elements to improve i18n and accessibility. Updated templates to bind [attr.lang] on spans, links and browse links; metadata-values now passes mdValue.language into render templates; clarin description component now emits per-entry language and value pairs; ClarinItemBoxView stores itemDescriptionLang; SidebarSearchListElement supports descriptionLang with a getDescriptionLang() hook and OrgUnit implementation returns the description language. These changes ensure displayed metadata uses the correct language attribute when available.

* Improve accessibility: add ARIA labels & i18n keys

Add ARIA attributes and move icon markup to improve screen-reader support across several components. Updated templates to include aria-label on interactive buttons (info, calendar, remove/save/delete/clear actions) and aria-hidden on decorative <i> icons. Adjusted calendar button markup to place the calendar icon inside the button element. Added corresponding i18n keys in many locale JSON5 files so the new labels are translatable (English provided and placeholder entries added across locales; some locales include TODO notes for translation). These changes are focused on accessibility and localization support.

* Improve accessibility and semantic headings

Replace several presentational heading tags with semantically appropriate heading levels (convert h4/h3/h5/h6 to h2/h3 with existing styling classes) across home, footer and item templates to improve document structure. Add aria-hidden to the hidden repository link and aria-live/aria-atomic to the pagination info for better accessibility. Update footer markup to group links into separate ULs (removing BRs) and adjust SCSS selectors to target both heading elements and .h4 class, plus spacing for stacked lists. Also add TranslateModule.forRoot() to an existing-relation-list-element spec to satisfy translations in tests.

* Update cs translations: relation group, license

Replace English strings with Czech translations in src/assets/i18n/cs.json5 for the following keys: form.relation-group.save, form.relation-group.delete, form.relation-group.clear, and submission.cc-license.field-info. Improves localization for relation-group actions and the CC license field.

* Mark decorative paperclip icon aria-hidden

Accessibility tweak: add aria-hidden="true" to the Font Awesome paperclip icon so screen readers ignore the decorative icon. Also replace the non-breaking space inside the <i> tag with a regular space between the icon and the heading text in clarin-files-section.component.html.

* Normalize metadata language to BCP 47 for HTML lang attributes

PR ufal/dspace-angular#139 binds [attr.lang] straight from the DSpace metadata language field,
which is stored as Java-style locales (en_US, cs_CZ) or the wildcard *. None
of those are valid BCP 47 / HTML lang values, so assistive technology ignores
them and the accessibility improvement is inert for the common stored values.

Add a single shared metadataLangToBcp47() helper (*/empty/nullish -> null,
"_" -> "-") and a thin dsLang pipe that delegates to it. Route all of PR #139's
lang bindings through it: the pipe in the seven templates, the helper directly
in the three .ts sites. Declare DsLangPipe in the specs that render the pipe
(NO_ERRORS_SCHEMA does not suppress missing pipes), including the shared grid
element test factory. Add unit specs for the helper and pipe plus a DOM
assertion that en_US renders as lang="en-US".

* Translate remaining English strings to Czech in cs.json5

---------



(cherry picked from commit cd1c6174bec756970e1240ecdfb1c9006cfe443a)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* [Port to dtq-dev] license management (#1299)

* 84 license labels management (ufal/dspace-angular#134)

* Add PUT and DELETE to ClarinLicenseLabel service

Replace BaseDataService with IdentifiableDataService and add PutData/DeleteData support for ClarinLicenseLabel. Wire PutDataImpl and DeleteDataImpl in the constructor, expose put(), delete() and deleteByHref() methods, and update imports (remove BaseDataService, add PutData, DeleteData, IdentifiableDataService and NoContent). Enables updating and deleting Clarin License Labels via the data service.

* Add license labels table and placeholders

Introduce a new License Labels section in the Clarin license table UI with a table, empty-state row, aria labels, and a ds-loading indicator (src/...component.html). Add CSS rule to size the actions column (src/...component.scss). In the component (src/...component.ts) add placeholder Observables (labels$, loading$) and stub methods editLabel and confirmDeleteLabel that log actions; data loading and wiring will be implemented in a follow-up task.

* Add label selection and refresh logic

Add selectable labels UI and wiring for label management:

- Template: add a radio column with aria labels, mark selected row, disable Edit/Delete unless a label is selected, and adjust colspan for empty state. Edit/Delete buttons now call no-arg handlers.
- Component: introduce selectedLabel, labels$ and loading$ BehaviorSubjects, refreshLabels() to fetch all labels via clarinLicenseLabelService.findAll (with loading/error handling), selectLabel() and isSelected() helpers, and call refreshLabels() on init and after label creation. Add ngUnsubscribe and ngOnDestroy to clean up subscriptions (takeUntil). Update scan initial state to use a defaultListState and import SortOptions.
- Tests: update spec to provide mockLicenseLabelListRD$ and spy findAll.

These changes enable selecting a license label for future edit/delete actions and keep the label list in sync with the backend.

* Add license labels management and i18n

Replace the static license-labels table with a paginated, RemoteData-driven table and translated headers; add loading state and accessible SR-only labels. Wire up edit and delete flows: open DefineLicenseLabelFormComponent for edits (convert file input, call PUT, notify and refresh) and ConfirmationModalComponent for deletes (call DELETE, notify and refresh). Introduce labelsRD$ stream, pagination options, labelsRefresh$ trigger and initializeLabelsPaginationStream to reactively fetch pages. Enhance DefineLicenseLabelFormComponent to support edit mode (prefill form, boolean extended options, aria/id fixes) and update serializer to accept booleans and legacy string values. Update and extend unit tests to cover edit/delete flows and template changes. Add new English and Czech i18n keys for the labels UI and actions.

* Use dedicated i18n key for label load errors

Introduce a specific i18n key for license label load failures and use it in the labels pagination stream. Added a labelsLoadErrorKey constant in ClarinLicenseTableComponent and replaced usages of the create-error key when handling load failures. Also added the new "clarin.license.label.load.error" translations to en.json5 and cs.json5.

* Refresh licenses after label update

Call loadAllLicenses() when a label PUT succeeds so dependent license lists are refreshed. The component now only calls refreshLabels() and loadAllLicenses() if the RemoteData indicates success. Updated unit test to spy on loadAllLicenses and assert it is called on modal submit; adjusted method comment accordingly.

* Reload licenses after label deletion

When a license label is successfully deleted, also reload the full license list to ensure UI reflects the change. Call loadAllLicenses() alongside refreshLabels() in the component, and update the spec to assert loadAllLicenses() is invoked (adding a spy and expectation).

* Hide empty table row while loading

Prevent the "no licenses" message from appearing while data is being fetched by adding a loading$ | async check to the empty-row *ngIf in clarin-license-table.component.html. The empty message now only shows when not loading and cLicenseLabels.page is empty, avoiding a flash of the empty state during load.

* Disable delete for labels in use with tooltip

Prevent deleting license labels that are referenced by any license: template changes wrap the delete button in a span that conditionally shows an ngbTooltip and uses dsBtnDisabled with a conditional click handler. Component logic now loads all licenses (paged) on init, builds a Set of in-use label IDs (inUseLabelIds) and exposes isLabelInUse to drive the UI; helper methods fetchAllLicensePages, rebuildLabelUsageSet and loadAllLicensesForUsage were added along with an allLicensesRD$ stream. Unit tests were extended to cover linked vs unlinked label behavior (disabled state, tooltip, and preventing the confirmation modal), and i18n entries for the disabled-tooltip were added for English and Czech.

* Show labels empty row only on successful load

Update clarin-license-table template to only render the labels empty-state row when the labels request has succeeded ((labelsRD$ | async)?.hasSucceeded). This prevents the empty message from appearing during failed or pending label requests. Add a unit test that simulates a failed labels request to verify the empty-state row is not shown, and import the needed createFailedRemoteDataObject helper for the test.

* Optimize license usage loading and tests

Add caching and locking around the expensive full license-usage crawl to avoid redundant requests. Introduce licenseUsageLoaded and licenseUsageLoading flags, a new ensureLicenseUsageLoaded(forceReload) helper, and a forceUsageReload parameter to loadAllLicenses so callers can explicitly invalidate the cache. Update create/edit/delete flows to force a usage reload, and set/clear the loading flags in the usage fetch success/error handlers. Add unit tests to verify that the full usage dataset is loaded only once across repeated reloads and that forcing a reload triggers a new fetch.

* F1: disable label Delete until usage crawl completes

The in-use set (inUseLabelIds) is empty until the async license usage
crawl finishes, so during that window an in-use label's Delete button
was briefly enabled. Add a usageReady$ stream that only emits true once
the crawl succeeds and gate every row's Delete button on it: all Delete
buttons stay disabled (with a "checking usage" tooltip) until usage
resolves, after which in-use labels stay disabled and unused ones enable.
On failure the buttons stay disabled so there is no unsafe window.

Frontend-only mitigation; the usage crawl itself is unchanged.

* F2: de-wrap double-nested modal in both license forms

Both define-license-form and define-license-label-form nested their own
.modal > .modal-dialog > .modal-content inside ng-bootstrap's
NgbModalWindow, which already provides those wrappers. The duplicate
chrome collapsed ngb's .modal-content and pinned the inner position:fixed
.modal to the top of the viewport instead of centering.

Remove the redundant inner wrappers so ngb owns the modal chrome, delete
the now-dead `.modal { display: ... }` scss workarounds in both forms,
fix the `modal-boy` -> `modal-body` typo in define-license-form, and open
all four form modals with { centered: true } so the dialog is centered.

* F3: surface newly created label without manual paging

The labels endpoint lists labels in ascending insertion order and ignores
the sort param, so a freshly created label received the highest id and
landed on the last pagination page, invisible to the admin after Save.
After a successful create, jump the labels table to the last page (derived
from the current total plus the one just added) so the new row is shown
immediately.

* F4: allow clearing a label icon on edit and preview the current icon

The label edit form could keep or replace an icon but never remove one,
and gave no indication of the current icon. Add a "Remove current icon"
checkbox and a small preview (reusing secureImageData) shown in edit mode
when the label has an icon. When clear is requested and no new file is
chosen, the table sends an empty icon array through the existing PUT path
to remove it; selecting a new file still takes precedence.

* F5: center the delete-confirmation modal and complete cs.json5 label parity

Pass { centered: true } when opening ConfirmationModalComponent from
confirmDeleteLabel() so the delete-confirmation dialog is vertically
centered like the four license/label form dialogs, removing the
top-aligned visual inconsistency.

Also add the three Czech translations that were missing from cs.json5
(table.delete.checking-tooltip, edit.icon.preview, edit.icon.clear) so
the en/cs label key sets are back in parity.

* test: expect centered option in delete-confirmation modal spec

The F5 change passes { centered: true } to modalService.open; update the
assertion to match so the label delete flow spec passes.

---------

Co-authored-by: kosarko <ko_ok@centrum.cz>
(cherry picked from commit 87cb7a7f007a0190894f4e4a772489a92a7c976b)

* Disable delete for in-use licenses, add tooltip (ufal/dspace-angular#137)

* Disable delete for in-use licenses, add tooltip

Prevent deleting licenses that are attached to bitstreams by disabling the Delete button and showing a tooltip explaining why. Adds UI markup (ngbTooltip wrapper, aria-label, dsBtnDisabled and guarded click), a new isSelectedLicenseInUse() helper and a check in deleteLicense(). Adds unit tests covering disabled/enabled states and click behavior, and provides i18n strings for English and Czech for the disabled-tooltip.

* Use hasNoValue for license id check

Replace isNull with hasNoValue when validating selectedLicense.id in deleteLicense to correctly handle undefined/null values. Also add hasNoValue to the imports from shared/empty.util.

* Use NgbTooltip instance in delete button spec

Update clarin-license-table component spec to import NgbTooltip and retrieve the tooltip instance from the delete wrapper's injector. Replace the previous assertion that inspected the DOM's ng-reflect-ngb-tooltip attribute with an assertion on deleteTooltip.ngbTooltip to verify the expected translation key. This makes the test more robust by avoiding reliance on Angular's rendered reflection attribute.

(cherry picked from commit 42c4fccba0bb87d9f2f05dd245745b194d599dff)

* Fix license label issues flagged in code review

- Change label icon fallback from selectedLabel.icon to undefined
  to avoid sending a base64 string when the icon is unchanged
- Correct 0‑based page parameter to 1‑based in fetchAllLicensePages
- Replace hard‑coded aria-label on modal close button with a
  translatable key for accessibility

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* [Port to dtq-dev] Add search icon to community and collection (ufal/dspace-angular#109) (#1326)

* add search icon to community and collection

add search icon to community and collection, click should navigate to community/collection pre-selected advance search page
add tests to confirm the new navigation

* Remove redundant wrapper div from collection/community page

Remove redundant wrapper div from collection/community page

(cherry picked from commit 385b33dc69d9569beb4529fe3863c4147b621a47)

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* test: fix flaky DSDynamicTypeBindRelationService spec (double spyOn) (#1338)

* test: fix flaky DSDynamicTypeBindRelationService spec (double spyOn)

The FormBuilderService mock (getMockFormBuilderService) is a jasmine.createSpyObj,
so getTypeBindModel / getTypeBindModelUpdates are already spies. Several specs then
call spyOn(...) on them again, which Jasmine rejects with
"<spyOn> : getTypeBindModel has already been spied upon". Under Karma/Jasmine's
randomized spec order this surfaced intermittently, so the same commit passed some
CI runs and failed others.

Reconfigure the existing spies via (spy as jasmine.Spy).and.returnValue/callFake(...)
instead of re-spying them (the standard idiom for a createSpyObj mock). The spy
behaviour per test is identical, so all assertions are unchanged; the illegal
double-spyOn is removed, making "already been spied upon" impossible in any order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: await TestBed setup in DSDynamicTypeBindRelationService spec

Per review: the first beforeEach did
`TestBed.configureTestingModule({...}).compileComponents().then();` — the promise
was never returned/awaited, so nothing guaranteed the module (and its fresh
getMockFormBuilderService() spy mock) was fully set up before each spec, which is
the kind of async setup slop that makes the same commit pass some CI runs and fail
others. Return the promise so Jasmine awaits it and every spec starts from a fresh,
fully-configured module. Complements the spy-reconfiguration fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: reword beforeEach comment (address Copilot review)

Replace the colloquial "async setup slop" wording with a clearer description
("letting asynchronous module setup leak between specs"). Comment-only change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* UFAL/Locale names for SimpleRORAuthority (ufal/dspace-angular#135) (#1337)

* Locale names for SimpleRORAuthority

(cherry picked from commit 1d367aa4692e4226da2e6cdbafe26ce08dcb43b5)

Co-authored-by: Milan Kuchtiak <kuchtiak@ufal.mff.cuni.cz>

* UFAL/fix(submission): keep dc.type dropdown display after change (ufal/dspace-angular#157) (#1336)

* fix(submission): keep dc.type dropdown display after change

(ufal/clarin-dspace#1377 -> ufal/dspace-angular#156)

The "type of resource" scrollable dropdown blanked out after changing its
value when the backend was slow: the reloaded model value came back with an
empty display but populated value/authority, so currentValue rendered ''.
Fall back to the underlying value when display is empty, matching the
existing inputFormatter convention.

Add a fakeAsync regression test that drives an empty-display value through
group.valueChanges (the real reload entry point) and asserts the rendered
input keeps its text, reproducing the original blank-after-latency bug.

* test(submission): make empty-display init test deterministic

Address Copilot PR review: the init-path test previously relied on
isNotEmpty(new VocabularyEntry()) being false so getInitValueFromModel would
fall back to the raw model.value. That couples the test to VocabularyEntry
having no initialized fields. Stub getInitValueFromModel directly to emit an
empty-display value (built bypassing the FFMVO constructor, which would
otherwise fill display via `display || value`), so the test deterministically
exercises the setCurrentValue fallback it is meant to guard.

* test(type-bind): stop re-spying mock methods that are already spies

The DSDynamicTypeBindRelationService spec called spyOn() on
formBuilderService.getTypeBindModel / getTypeBindModelUpdates, but the mock
(getMockFormBuilderService) already creates those as jasmine spies via
createSpyObj. Because src/test.ts runs with teardown.destroyAfterEach:false
and karma randomizes spec order, certain orderings hit the second spy
installation and threw "getTypeBindModel has already been spied upon",
failing 6 tests non-deterministically (green on clarin-v7, red here only due
to a different random ordering). Reconfigure the existing spies with .and.*
instead of re-spying, which is order-independent.

(cherry picked from commit 8580e90ae91edce451258ffdf01568ee8bbb0af0)

* UFAL/[Port to dtq-dev] 117 better control of user deletion in user administration page (#1335)

* 117 better control of user deletion in user administration page (ufal/dspace-angular#140)

* Disable self-delete and add delete warnings

Prevent administrators from deleting their own account by disabling the delete button for the current authenticated user and showing a tooltip. Add a warningLabel input to the confirmation modal and render it when present. Implement logic in EPeopleRegistryComponent to determine the current user, compute a contextual delete warning (submitter, admin, or both) by querying workspace/workflow submissions, search, and group membership, and open the confirmation modal with that warning. Handle delete responses to show a friendly notification for backend 400 self-delete errors and use a generic failure notification otherwise. Update templates, component imports and helper methods (isCurrentUser, getDeleteWarningLabel, hasSubmittedItems, isAdministrator, isSelfDeletionError, showSelfDeleteNotification), and extend unit tests to cover disabled self-delete UI, composed warning labels, and notification behavior.

* Prevent self-delete and show delete warnings

Disable direct self-deletion and surface contextual warnings when deleting an EPerson. Template: render the delete button as disabled for the currently authenticated user and show a tooltip explaining self-delete restrictions; otherwise show the normal delete button. Component: track the current authenticated user, compute a combined warning label based on whether the EPerson is an administrator and/or has submitted items (checks workspace, workflow and archived submissions), present that warning in the confirmation modal, and handle deletion results including a friendly notification for backend self-delete errors. Added helper methods (isCurrentUser, getDeleteWarningLabel, hasSubmittedItems, isAdministrator, isSelfDeletionError, showSelfDeleteNotification) and adjusted delete flow to update canDelete$ appropriately. Tests: updated and added specs and mocks to cover the disabled self-delete UI, combined warning label usage, and the friendly self-delete error notification; added required service mocks and test setup changes.

* Add EPerson delete warnings and self-delete message

Add four i18n keys to en.json5 and cs.json5 for EPerson deletion flows: a forbidden self-delete message and warnings for deleting users who are submitters, administrators, or both. Provides localized English and Czech strings to surface these messages in the admin access-control UI.

* Guard EPerson delete until auth ID resolved

Prevent delete actions from running before the current authenticated user id is available. Add a template condition to hide the delete button until currentAuthenticatedUserId is set, add an early-return guard in deleteEPerson when the id is missing, and add a unit test ensuring the modal is not opened and delete is not called before the id is resolved.

* Hide delete button until auth user ID resolved

Prevent delete actions before the current authenticated user ID is available. Add *ngIf to the delete button in the template, add an early return guard in the component's delete flow when currentAuthenticatedUserId is not set, and include unit tests verifying the button is hidden and the delete/modal are not invoked until the ID is resolved.

* Restrict delete button visibility and update tests

Template: Wrap self-delete tooltip/button in a guard that checks epersonDto.ableToDelete and currentAuthenticatedUserId so delete UI is only rendered when deletion is allowed; simplify the enabledDeleteButton markup by removing a duplicated *ngIf on the inner button. Spec: remove unused DebugElement import, rename the test from 'should be disabled' to 'should be hidden', and update assertions to expect no delete buttons to be present (reflecting the new visibility behavior).

* Detect Administrator group across paginated pages

Add support for detecting Administrator group membership across paginated group lists and a unit test for it.

- Introduce hasAdministratorGroupOnPage(groupsHref, currentPage) which fetches group pages (elementsPerPage=100) and recursively checks subsequent pages when the Administrator group is not found on the current page.
- isAdministrator now delegates to hasAdministratorGroupOnPage starting at page 1.
- Preserve error handling to return false on failures.
- Add a unit test that simulates a two-page group response (administrator present on the second page) and verifies the component detects administrator membership and displays the correct warning label.

* Detect Administrator group across pages

Refactor group membership check to traverse paginated group lists until an "Administrator" group is found or pages are exhausted. Introduces a recursive hasAdministratorGroupOnPage that requests pages, validates payload/pageInfo, checks for the admin group, and requests the next page when needed. Adds a unit test covering detection on later pages and a minor spec setup tweak (setting currentAuthenticatedUserId) to correctly exercise deletion behavior.

* Fix Czech typo in delete warning

Corrects a spelling mistake in src/assets/i18n/cs.json5 for key admin.access-control.epeople.delete.warning.submitter: changed 'repositáři' to 'repozitáři' to improve Czech translation accuracy.

* Make disabled delete button unfocusable

Add tabindex="-1" to the disabled delete button in the epeople registry template so it cannot receive keyboard focus. This ensures the parent span (which provides the tooltip) remains the sole focus target for the current-user case, improving accessibility and preventing duplicate focusable elements.

* Make disabled self-delete button unfocusable

Add tabindex="-1" to the disabled delete button in eperson-form.component.html so the button cannot receive keyboard focus when showing the current user. The tooltip remains on the parent span, improving keyboard navigation and accessibility for the self-delete warning.

* Remove stray 'after' attribute from eperson template

Remove an unintended "after" attribute from a <span> in eperson-form.component.html. The change cleans up the template by leaving only the intended *ngIf binding for conditional rendering of the delete/impersonation UI, preventing potential template parsing or linting issues.

* code cleanup - remove duplicities

* cleanup

* copilot comments

* copilot's comment

---------

Co-authored-by: Ondrej Kosarko <kosarko@ufal.mff.cuni.cz>
(cherry picked from commit d0b5ecf7b70ef4d40fbc4d8a968fc7c1eee273dd)

* fix(access-control): quote submitter UUID in archived item search query

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* UFAL/[Port to dtq-dev] 151 support for ror identifiers (#1339)

* 151 support for ror identifiers (ufal/dspace-angular#153)

* fix(item-view): use authority value for publisher search links (ROR)

fix(item-view): use authority value for publisher search links (ROR)

* fix(search-results): use authority value for publisher links on result cards

fix(search-results): use authority value for publisher links on result cards

* feat(ror): add ROR icon to publisher links on item view and search cards

feat(ror): add ROR icon to publisher links on item view and search cards

* fix(ror-itempage-search-results): put the image in the publisher redirect

fix(ror-itempage-search-results): put the image in the publisher redirect

* fix(ror-icon): add icon in the item page

fix(ror-icon): add icon in the item page

* fix(lint): remove redundant type annotation for hasPublisherAuthority

fix(lint): remove redundant type annotation for hasPublisherAuthority

* feat(ror): add ROR icon and authority-based publisher search links

feat(ror): add ROR icon and authority-based publisher search links

* 153 ror shared helper (ufal/dspace-angular#159)

* refactor(ror): extract shared authority search-filter helper, drop creativework.publisher

Follow-up cleanup on the ROR publisher support (PR ufal/dspace-angular#153):

- Add buildAuthoritySearchFilter() in clarin-shared-util.ts and use it from
  getLinkToSearch (generic item field), the search-result box view, and
  loadItemAuthors, so the authority-vs-equals operator logic lives in one place.
- Drop creativework.publisher from the CLARIN item view (untyped-item fields,
  the ROR icon condition and convertMetadataFieldIntoSearchType) so the item
  page and the search card agree on dc.publisher. Journal entity pages, which
  use the upstream ds-generic-item-page-field, are unaffected.
- Add a clarin-generic-item-field spec covering getLinkToSearch branches and a
  unit test for the new helper.

* don't remove the creativework.publisher

* Avoid transient broken publisher href during async base-URL fetch

Use [attr.href] instead of [href] so Angular omits the attribute while
publisherRedirectLink is still undefined (before assignBaseUrl resolves),
rendering the publisher as plain text until the URL is ready.

---------

Co-authored-by: Ondřej Košarko <kosarko@ufal.mff.cuni.cz>
(cherry picked from commit 5bab4f13b0c46190619c2ef0e506ffff95fa676d)

* fix: prevent undefined value in authority search filter encoding

---------

Co-authored-by: Amad Ul Hassan <hassan@ufal.mff.cuni.cz>

* Fix home-page SSR->CSR flicker (#1287)

* Fix home-page SSR->CSR flicker

Angular 15 has no provideClientHydration; on every browser load Angular
tears down the entire SSR DOM and rebuilds the component tree from scratch.
Measured CLS = 0.89 at t=1.76s on /home (PerformanceObserver on dev-5).
The visible flicker is that ~600ms rebuild window between SSR view and
populated CSR view.

Two compounding causes, addressed in this PR:

1. CustomEagerThemeModule was commented out in src/themes/eager-themes.module.ts,
   so every custom-themed wrapper (footer, header, root, ...) was lazy-loaded via
   webpack code-splitting on the browser, stretching the gap. Re-enable it
   (the existing custom/eager-theme.module.ts already declares the right set).
   Bumps initial bundle by ~256KB; angular.json budget raised from 5MB to 8MB
   to accommodate.

2. The bigger cause - no hydration - is masked by an inline pre-bootstrap script
   in src/index.html that:
     - Captures all <style ng-transition="dspace-angular"> blocks into
       <style data-dspace-ssr-keep> tags Angular won't strip (Angular removes
       the originals on bootstrap, which is why a naive overlay renders
       unstyled).
     - Moves (not clones) the SSR-rendered <ds-app> children into an
       absolute-positioned overlay so they keep every live DOM/style detail.
     - Hides the now-empty <ds-app> via a data-attribute and CSS rule.
     - Exposes window.__dspaceRemoveSsrOverlay() for AppComponent to call
       once ApplicationRef.isStable fires (with one rAF + 50ms pad).
     - 15s safety fallback in case isStable never fires.

Bots and no-JS users still get the original SSR <ds-app> (the overlay is
JS-added). Real users see continuous SSR-rendered content while CSR rebuilds
invisibly underneath, then a 150ms fade reveals the CSR DOM in its final
data-loaded state.

Verified locally via Service Worker that suppresses the removal: overlay's
header height is 80px (proper styling preserved) versus 698px (the unstyled
fallback before this fix's style-preservation step).

Includes a small Windows cmd deploy helper at scripts/dspace-deploy.bat and
matching skill doc at .claude/skills/dspace-deploy/SKILL.md - multi-instance
safe local dev stack via the existing docker compose files.

* Lint: curly braces around early-return if

* Address Copilot review

- angular.json: tighten budget back to 5.5MB warn / 6MB error (was 8MB)
- index.html: re-entrancy guard on __dspaceRemoveSsrOverlay (null the
  pointer up-front so the isStable + 15s safety fallback can't double-fade)
- index.html: drop aria-hidden from overlay so screen-reader users get the
  SSR snapshot during boot (ds-app underneath has visibility:hidden which
  already excludes it from a11y tree)
- index.html: console.warn on the overlay-script catch so a silently broken
  flicker fix is at least diagnosable in DevTools
- typings.d.ts: typed Window.__dspaceRemoveSsrOverlay augmentation; drop
  the `as any` cast in AppComponent.removeSsrOverlayWhenStable
- app.component.spec.ts: cover removeSsrOverlayWhenStable (calls the
  global once on isStable=true; no-op when global absent)
- Drop scripts/dspace-deploy.bat + .claude/skills/dspace-deploy/SKILL.md
  from this PR per request (local dev tooling, will live elsewhere)

* Disable SSR overlay when Cypress is driving

The overlay holds the SSR-rendered children alongside <ds-app>'s CSR-rendered
children during the masking window. Cypress's cy.get(selector) sees both
copies, so unique-id selectors return 2 elements and cy.click() fails. The
overlay is purely a UX smoothing layer (no behaviour to E2E-validate), so
short-circuit when window.Cypress is present. Browser users are unaffected.

* Advance root flicker fix to the final DOM-settle overlay mechanism (#1318, #1321)

The original overlay removal waited for ApplicationRef.isStable, which is held
hostage by post-login/admin zone activity (page looks rendered but is not
interactive - dspace-customers#725). Replace it with the final mechanism used
across the customer backports: keep the SSR snapshot until the routed <ds-app>
DOM has settled (MutationObserver + quiet window, content-height / #main-content
check, 10s cap), and make the overlay a purely visual mask so the live app stays
interactive underneath. index.html / app.component.ts / spec / typings are synced
to the final version; the existing ngAfterViewInit delay(0) is preserved.

Keeping the trunk on the final mechanism means new customer branches cut from
dtq-dev start correct instead of re-inheriting the old isStable approach.

Ref: #1318, #1321

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: jm <jm@maz>
Co-authored-by: Juraj Roka <95219754+jr-rk@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Skills/Update .dspace-skills submodule to the latest main (#1348)

* Skills/Update .dspace-skills submodule to latest main (#1349)

* UFAL/Backport #1333: admin-sidebar gutter via CSS var (no logged-in reload shift) (#1350)

Backport of dataquest-dev/dspace-angular#13…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the "New version" button to workspace (your submissions)

3 participants