Skip to content

Commit 34d32e2

Browse files
authored
Merge pull request #43 from ufal/dataquest/dtq-dev
dataquest -> lindat sync (lindat-2025.07.16502729677)
2 parents 1dff30d + b05df42 commit 34d32e2

9 files changed

Lines changed: 116 additions & 193 deletions

File tree

.github/workflows/trigger-ui-tests.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ jobs:
2424
2525
git fetch --prune origin
2626
BRANCHES=$(git ls-remote --heads origin | awk -F'/' '{print $3"/"$4}' | grep '^customer/')
27+
SKIP_BRANCHES=("customer/sav" "customer/vsb-tuo" "customer/zcu-data")
28+
2729
for branch in $(echo "$BRANCHES" | sed -e 's/[\[\]"]//g' -e 's/,/\n/g'); do
30+
if [[ " ${SKIP_BRANCHES[@]} " =~ " ${branch} " ]]; then
31+
echo "Skipping branch $branch"
32+
continue
33+
fi
34+
2835
echo "Triggering UI tests for branch: $branch"
2936
gh workflow run playwright-tests.yml --ref $branch
3037
done

src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component, Input, OnInit } from '@angular/core';
22
import { Bitstream } from '../../core/shared/bitstream.model';
3-
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
4-
import { finalize, switchMap, take } from 'rxjs/operators';
3+
import { BehaviorSubject, firstValueFrom, Observable, of as observableOf } from 'rxjs';
4+
import { filter, finalize, switchMap, take } from 'rxjs/operators';
55
import { followLink } from '../../shared/utils/follow-link-config.model';
66
import { ClarinUserRegistration } from '../../core/shared/clarin/clarin-user-registration.model';
77
import { ClarinUserMetadata } from '../../core/shared/clarin/clarin-user-metadata.model';
@@ -35,13 +35,13 @@ import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-bu
3535
import { HttpOptions } from '../../core/dspace-rest/dspace-rest.service';
3636
import { Router } from '@angular/router';
3737
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';
38-
import { getBitstreamContentRoute } from '../../app-routing-paths';
3938
import { hasFailed } from 'src/app/core/data/request-entry-state.model';
4039
import { FindListOptions } from '../../core/data/find-list-options.model';
4140
import isEqual from 'lodash/isEqual';
4241
import cloneDeep from 'lodash/cloneDeep';
4342
import { ClarinUserMetadataDataService } from '../../core/data/clarin/clarin-user-metadata.service';
4443
import { HtmlContentService } from '../../shared/html-content.service';
44+
import { FileService } from '../../core/shared/file.service';
4545

4646
/**
4747
* The component shows the user's filled in user metadata and the user can fill in other required user metadata.
@@ -143,7 +143,9 @@ export class ClarinLicenseAgreementPageComponent implements OnInit {
143143
private hardRedirectService: HardRedirectService,
144144
private requestService: RequestService,
145145
private clarinUserMetadataDataService: ClarinUserMetadataDataService,
146-
private htmlContentService: HtmlContentService) { }
146+
private htmlContentService: HtmlContentService,
147+
protected fileService: FileService,
148+
protected notificationsService: NotificationsService) { }
147149

148150
ngOnInit(): void {
149151
// Load CurrentItem by bitstreamID to show itemHandle
@@ -242,7 +244,7 @@ export class ClarinLicenseAgreementPageComponent implements OnInit {
242244
} else {
243245
// Or just download the bitstream by download token
244246
const downloadToken = Object.values(responseRD$?.payload).join('');
245-
this.redirectToDownload(downloadToken);
247+
void this.redirectToDownload(downloadToken);
246248
}
247249
});
248250
}
@@ -255,23 +257,33 @@ export class ClarinLicenseAgreementPageComponent implements OnInit {
255257
return this.router.routerState.snapshot.url.endsWith('/zip');
256258
}
257259

258-
private redirectToDownload(downloadToken = null) {
259-
// 1. Get bitstream
260-
// 2. Get bitstream download link
261-
// 3. Get bitstream content download link and check if there is `authorization-token` in to query params
262-
let bitstream = null;
263-
this.bitstream$
264-
.pipe(take(1))
265-
.subscribe(bitstream$ => {
266-
bitstream = bitstream$;
267-
});
268-
let bitstreamDownloadPath = getBitstreamContentRoute(bitstream);
269-
if (isNotEmpty(downloadToken)) {
270-
bitstreamDownloadPath = this.halService.getRootHref() + '/core' + bitstreamDownloadPath +
271-
'?dtoken=' + downloadToken;
260+
/**
261+
* Redirects to the download link of the bitstream.
262+
* If a download token is provided, it appends it as a query parameter.
263+
*
264+
* @param downloadToken
265+
* @private
266+
*/
267+
private async redirectToDownload(downloadToken?: string): Promise<void> {
268+
try {
269+
const bitstream = await firstValueFrom(this.bitstream$.pipe(take(1)));
270+
271+
const fileLink = await firstValueFrom(
272+
this.fileService.retrieveFileDownloadLink(bitstream._links.content.href).pipe(
273+
filter(hasValue),
274+
take(1)
275+
)
276+
);
277+
278+
// Determine whether the URL already contains query parameters
279+
const hasQueryParams = fileLink.includes('?');
280+
const tokenParam = downloadToken ? `${hasQueryParams ? '&' : '?'}dtoken=${downloadToken}` : '';
281+
282+
const redirectUrl = `${fileLink}${tokenParam}`;
283+
this.hardRedirectService.redirect(redirectUrl);
284+
} catch (error) {
285+
this.notificationsService.error(this.translateService.instant('clarin-license-agreement-page.download-error'));
272286
}
273-
274-
this.hardRedirectService.redirect(bitstreamDownloadPath);
275287
}
276288

277289
public getMetadataValueByKey(metadataKey: string) {

src/app/core/data/dso-redirect.service.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/* eslint-disable max-classes-per-file */
99
import { Injectable, Inject } from '@angular/core';
1010
import { Observable } from 'rxjs';
11-
import { tap } from 'rxjs/operators';
11+
import { take, tap } from 'rxjs/operators';
1212
import { hasValue } from '../../shared/empty.util';
1313
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
1414
import { ObjectCacheService } from '../cache/object-cache.service';
@@ -19,7 +19,7 @@ import { RequestService } from './request.service';
1919
import { getFirstCompletedRemoteData } from '../shared/operators';
2020
import { DSpaceObject } from '../shared/dspace-object.model';
2121
import { IdentifiableDataService } from './base/identifiable-data.service';
22-
import { getDSORoute } from '../../app-routing-paths';
22+
import { getDSORoute, getForbiddenRoute } from '../../app-routing-paths';
2323
import { HardRedirectService } from '../services/hard-redirect.service';
2424
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
2525
import { Router } from '@angular/router';
@@ -45,7 +45,7 @@ class DsoByIdOrUUIDDataService extends IdentifiableDataService<DSpaceObject> {
4545
// interpolate id/uuid as query parameter
4646
(endpoint: string, resourceID: string): string => {
4747
return endpoint.replace(/{\?id}/, `?id=${resourceID}`)
48-
.replace(/{\?uuid}/, `?uuid=${resourceID}`);
48+
.replace(/{\?uuid}/, `?uuid=${resourceID}`);
4949
},
5050
);
5151
}
@@ -108,12 +108,23 @@ export class DsoRedirectService {
108108
}
109109
}
110110
}
111-
// Redirect to login page if the user is not authenticated to see the requested page
111+
// Handle authentication errors: redirect unauthenticated users to login, authenticated users to forbidden page
112112
if (response.hasFailed && (response.statusCode === 401 || response.statusCode === 403)) {
113-
// Extract redirect URL - remove `https://.../namespace` from the current URL. Keep only `handle/...`
114-
const redirectUrl = this.extractHandlePath(window.location.href);
115-
this.authService.setRedirectUrl(redirectUrl);
116-
this.router.navigateByUrl('login');
113+
const isAuthenticated$ = this.authService.isAuthenticated();
114+
isAuthenticated$
115+
.pipe(take(1))
116+
.subscribe((isAuthenticated) => {
117+
if (!isAuthenticated) {
118+
// If the user is not authenticated, redirect to login page
119+
// Extract redirect URL - remove `https://.../namespace` from the current URL. Keep only `handle/...`
120+
const redirectUrl = this.extractHandlePath(window.location.href);
121+
this.authService.setRedirectUrl(redirectUrl);
122+
void this.router.navigateByUrl('login');
123+
} else {
124+
// If the user is authenticated but still has no access, redirect to forbidden page
125+
void this.router.navigateByUrl(getForbiddenRoute());
126+
}
127+
});
117128
}
118129
})
119130
);

src/app/item-page/clarin-ref-citation/clarin-ref-citation.component.html

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@
1111
</div>
1212
</div>
1313
<div class="pt-2 clarin-ref-box-body d-flex justify-content-between pl-2" >
14-
<div class="clarin-ref-box-text pr-4">
15-
<span>{{citationText + ', '}}</span>
16-
<span><i>{{itemNameText + ', '}}</i></span>
17-
<span>{{repositoryNameText + ', '}}</span>
18-
<span><a [href]="identifierURI">{{prettifiedIdentifier | async}}</a></span>
14+
<div class="clarin-ref-box-text pr-4" [innerHTML]="refboxContent | async">
1915
</div>
2016
<div class="clarin-ref-box-copy-wrapper" placement="bottom" [ngbTooltip]="'Copied!'" #tooltip="ngbTooltip"
2117
(click)="copyText()">

0 commit comments

Comments
 (0)