From 6b6219e801e65723d82f902ab109acbb29cbc063 Mon Sep 17 00:00:00 2001 From: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com> Date: Fri, 15 Dec 2023 08:23:01 +0100 Subject: [PATCH] Iteration 5 (#426) * by default only deploy to INSTANCE=5 * give more time for postgres init * add even more time * Update action.yml (#405) Run import from main branch * ufal/fe-update-json-messages Added missing tranlates and updated messages after running the script * ufal/fe-initiated-login-not-redirecting-from-collection Redirect to login with `redirectUrl` param because it is lost after click on `local` login button. (#404) * [devOps] add dspace commands after import * ufal/fe-add-default-static-pages (#406) * Added static pages from the lindat git * Fixed redirection and created safeHtml pipe. * Could redirect out of namespace url, small refactoring. * Updated tests. * Added images into the static pages. * ufal/fe-shibboleth-validate-emails * Encoded query params (#407) * placeholder configs to be mounted into docker * change entrypoint so that containers has less logs and restarts (#412) * ufal/fe-get-user-ip-address (#420) * The clients IP address is fetched from the BE API. * Replace LegacyBitstreamUrlResolver by BitstreamBreadcrumbResolver because the first one throws errors to the console. * ufal/fe-fix-static-page-redirect (#421) * Fixed static page redirects. * Refactored processing links. * ufal/fe-download-bitstream-back-to-item (#423) * Separated toggling of DiscoJuice popup from the workign with redirect URL * The downloading page is loaded before downloading. * Login redirect works. * Removed storing cookies in the aai.js --------- Co-authored-by: MajoBerger <88670521+MajoBerger@users.noreply.github.com> Co-authored-by: Jozef Misutka <332350+vidiecan@users.noreply.github.com> Co-authored-by: jm Co-authored-by: MajoBerger --- .../bitstream-page-routing.module.ts | 2 +- ...larin-bitstream-download-page.component.ts | 4 +- ...clarin-license-agreement-page.component.ts | 30 ++++++- .../password/log-in-password.component.ts | 28 +++--- .../static-page/static-page-routing-paths.ts | 1 + src/app/static-page/static-page.component.ts | 89 +++++++++++++++---- 6 files changed, 119 insertions(+), 35 deletions(-) diff --git a/src/app/bitstream-page/bitstream-page-routing.module.ts b/src/app/bitstream-page/bitstream-page-routing.module.ts index cdb0c320c81..3b0fa6caa69 100644 --- a/src/app/bitstream-page/bitstream-page-routing.module.ts +++ b/src/app/bitstream-page/bitstream-page-routing.module.ts @@ -47,7 +47,7 @@ const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations'; // component: BitstreamDownloadPageComponent, resolve: { bitstream: BitstreamPageResolver, - breadcrumb: LegacyBitstreamUrlResolver + breadcrumb: BitstreamBreadcrumbResolver }, }, { diff --git a/src/app/bitstream-page/clarin-bitstream-download-page/clarin-bitstream-download-page.component.ts b/src/app/bitstream-page/clarin-bitstream-download-page/clarin-bitstream-download-page.component.ts index a1988387dd6..b9e5c356b35 100644 --- a/src/app/bitstream-page/clarin-bitstream-download-page/clarin-bitstream-download-page.component.ts +++ b/src/app/bitstream-page/clarin-bitstream-download-page/clarin-bitstream-download-page.component.ts @@ -126,10 +126,10 @@ export class ClarinBitstreamDownloadPageComponent implements OnInit { // bitstreamURL = 'http://localhost:8080/server/api/core/bitstreams/d9a41f84-a470-495a-8821-20e0a18e9276/content'; if ((isAuthorized || isAuthorizedByClarin) && isLoggedIn && isNotEmpty(fileLink)) { this.downloadStatus.next(RequestEntryState.Success); - this.hardRedirectService.redirect(fileLink); + window.location.replace(fileLink); } else if ((isAuthorized || isAuthorizedByClarin) && !isLoggedIn) { this.downloadStatus.next(RequestEntryState.Success); - this.hardRedirectService.redirect(bitstreamURL); + window.location.replace(bitstreamURL); } else if (!(isAuthorized || isAuthorizedByClarin) && isLoggedIn && this.downloadStatus.value === RequestEntryState.Error) { // this.downloadStatus is `ERROR` - no CLARIN exception is thrown up diff --git a/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.ts b/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.ts index bc8855fd8fe..a0e1cb3842c 100644 --- a/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.ts +++ b/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.ts @@ -8,7 +8,7 @@ import { ClarinUserMetadata } from '../../core/shared/clarin/clarin-user-metadat import { getFirstCompletedRemoteData, getFirstSucceededRemoteListPayload } from '../../core/shared/operators'; import { RequestParam } from '../../core/cache/models/request-param.model'; import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; -import { PostRequest } from '../../core/data/request.models'; +import { GetRequest, PostRequest } from '../../core/data/request.models'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { AuthService } from '../../core/auth/auth.service'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; @@ -384,9 +384,29 @@ export class ClarinLicenseAgreementPageComponent implements OnInit { ); } + /** + * Load the user IP Address by API + * */ private loadIPAddress() { - this.http.get('http://api.ipify.org/?format=json').subscribe((res: any) => { - this.ipAddress$.next(res.ip); + const requestId = this.requestService.generateRequestId(); + + const url = this.halService.getRootHref() + '/userinfo/ipaddress'; + const getRequest = new GetRequest(requestId, url); + // Send GET request + this.requestService.send(getRequest); + // Get response + const response = this.rdbService.buildFromRequestUUID(requestId); + response + .pipe(getFirstCompletedRemoteData()) + .subscribe((responseRD$: RemoteData) => { + if (hasFailed(responseRD$.state)) { + this.error$.value.push('Cannot load the IP Address'); + return; + } + if (isEmpty(responseRD$?.payload)) { + return; + } + this.ipAddress$.next(responseRD$?.payload?.ipAddress); }); } @@ -411,3 +431,7 @@ export class ClarinLicenseAgreementPageComponent implements OnInit { this.helpDesk$ = this.configurationDataService.findByPropertyName(HELP_DESK_PROPERTY); } } + +interface IPAddress { + ipAddress: string; +} diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.ts b/src/app/shared/log-in/methods/password/log-in-password.component.ts index e0d02822230..f59419e901d 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.ts @@ -138,6 +138,7 @@ export class LogInPasswordComponent implements OnInit { // Load `dspace.ui.url` into `baseUrl` property. await this.assignBaseUrl(); + this.toggleDiscojuiceLogin(); void this.setUpRedirectUrl(); } @@ -155,13 +156,14 @@ export class LogInPasswordComponent implements OnInit { } // Store the `redirectUrl` value from the url and then remove that value from url. - if (isNotEmpty(this.route.snapshot.queryParams?.redirectUrl)) { - // Overwrite `this.redirectUrl` only if it's not stored in the authService `redirectUrl` property. - if (isEmpty(this.redirectUrl)) { - this.redirectUrl = this.route.snapshot.queryParams?.redirectUrl; - } - } else { - // Pop up discojuice login e.g. when the token is expired or the user is trying to download restricted bitstream. + // Overwrite `this.redirectUrl` only if it's not stored in the authService `redirectUrl` property. + if (isEmpty(this.redirectUrl)) { + this.redirectUrl = this.route.snapshot.queryParams?.redirectUrl; + } + } + + private toggleDiscojuiceLogin() { + if (isEmpty(this.route.snapshot.queryParams?.redirectUrl)) { this.popUpDiscoJuiceLogin(); } } @@ -191,10 +193,16 @@ export class LogInPasswordComponent implements OnInit { email.trim(); password.trim(); - // Local authentication redirects to /login page and the user should be redirected to the page from where - // was the login initiated. if (!this.isStandalonePage || isNotEmpty(this.redirectUrl)) { - this.authService.setRedirectUrl(this.redirectUrl.replace(this.baseUrl, '')); + // Create a URLSearchParams object + const urlParams = new URLSearchParams(this.redirectUrl.split('?')[1]); + // Get the value of the 'redirectUrl' parameter + let redirectUrl = urlParams.get('redirectUrl'); + if (isEmpty(redirectUrl)) { + redirectUrl = this.redirectUrl; + } + + this.authService.setRedirectUrl(redirectUrl.replace(this.baseUrl, '')); } else { this.authService.setRedirectUrlIfNotSet('/'); } diff --git a/src/app/static-page/static-page-routing-paths.ts b/src/app/static-page/static-page-routing-paths.ts index 8df2e5bcd36..e6fd63cf4d2 100644 --- a/src/app/static-page/static-page-routing-paths.ts +++ b/src/app/static-page/static-page-routing-paths.ts @@ -3,4 +3,5 @@ */ export const STATIC_PAGE_PATH = 'static'; export const STATIC_FILES_PROJECT_PATH = 'static-files'; +export const HTML_SUFFIX = '.html'; export const STATIC_FILES_DEFAULT_ERROR_PAGE_PATH = STATIC_FILES_PROJECT_PATH + '/' + 'error.html'; diff --git a/src/app/static-page/static-page.component.ts b/src/app/static-page/static-page.component.ts index 63aac66bc3a..c44d5fb6ebd 100644 --- a/src/app/static-page/static-page.component.ts +++ b/src/app/static-page/static-page.component.ts @@ -4,7 +4,11 @@ import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { Router } from '@angular/router'; import { isEmpty, isNotEmpty } from '../shared/empty.util'; import { LocaleService } from '../core/locale/locale.service'; -import { STATIC_FILES_DEFAULT_ERROR_PAGE_PATH, STATIC_FILES_PROJECT_PATH } from './static-page-routing-paths'; +import { + HTML_SUFFIX, + STATIC_FILES_DEFAULT_ERROR_PAGE_PATH, + STATIC_FILES_PROJECT_PATH, STATIC_PAGE_PATH +} from './static-page-routing-paths'; import { APP_CONFIG, AppConfig } from '../../config/app-config.interface'; /** @@ -39,6 +43,8 @@ export class StaticPageComponent implements OnInit { // Compose url url = STATIC_FILES_PROJECT_PATH; url += isEmpty(language) ? '/' + this.htmlFileName : '/' + language + '/' + this.htmlFileName; + // Add `.html` suffix to get the current html file + url = url.endsWith(HTML_SUFFIX) ? url : url + HTML_SUFFIX; let potentialContent = await firstValueFrom(this.htmlContentService.fetchHtmlContent(url)); if (isNotEmpty(potentialContent)) { this.htmlContent.next(potentialContent); @@ -57,25 +63,70 @@ export class StaticPageComponent implements OnInit { await this.loadErrorPage(); } - processLinks(e) { - const element: HTMLElement = e.target; - if (element.nodeName === 'A') { - e.preventDefault(); - const href = element.getAttribute('href')?.replace('/', ''); - let redirectUrl = window.location.origin + this.appConfig.ui.nameSpace + '/static/'; - // Start with `#` - redirect to the fragment - if (href.startsWith('#')) { - redirectUrl += this.htmlFileName + href; - } else if (href.startsWith('.')) { - // Redirect using namespace e.g. `./test.html` -> `/namespace/static/test.html` - redirectUrl += href.replace('.', '') + '.html'; - } else { - // Redirect without using namespace e.g. `/test.html` -> `/test.html` - redirectUrl = redirectUrl.replace(this.appConfig.ui.nameSpace, '') + href; - } - // Call redirect - window.location.href = redirectUrl; + /** + * Handle click on links in the static page. + * @param event + */ + processLinks(event: Event): void { + const targetElement = event.target as HTMLElement; + + if (targetElement.nodeName !== 'A') { + return; } + + event.preventDefault(); + + const href = targetElement.getAttribute('href'); + const { nameSpace } = this.appConfig.ui; + const namespacePrefix = nameSpace === '/' ? '' : nameSpace; + + const redirectUrl = this.composeRedirectUrl(href, namespacePrefix); + + if (this.isFragmentLink(href)) { + this.redirectToFragment(redirectUrl, href); + } else if (this.isRelativeLink(href)) { + this.redirectToRelativeLink(redirectUrl, href); + } else if (this.isExternalLink(href)) { + this.redirectToExternalLink(href); + } else { + this.redirectToAbsoluteLink(redirectUrl, href, namespacePrefix); + } + } + + private composeRedirectUrl(href: string | null, namespacePrefix: string): string { + const staticPagePath = STATIC_PAGE_PATH; + const baseUrl = new URL(window.location.origin); + baseUrl.pathname = `${namespacePrefix}/${staticPagePath}/`; + return baseUrl.href; + } + + private isFragmentLink(href: string | null): boolean { + return href?.startsWith('#') ?? false; + } + + private redirectToFragment(redirectUrl: string, href: string | null): void { + window.location.href = `${redirectUrl}${this.htmlFileName}${href}`; + } + + private isRelativeLink(href: string | null): boolean { + return href?.startsWith('.') ?? false; + } + + private redirectToRelativeLink(redirectUrl: string, href: string | null): void { + window.location.href = new URL(href, redirectUrl).href; + } + + private isExternalLink(href: string | null): boolean { + return (href?.startsWith('http') || href?.startsWith('www')) ?? false; + } + + private redirectToExternalLink(href: string | null): void { + window.location.replace(href); + } + + private redirectToAbsoluteLink(redirectUrl: string, href: string | null, namespacePrefix: string): void { + const absoluteUrl = new URL(href, redirectUrl.replace(namespacePrefix, '')); + window.location.href = absoluteUrl.href; } /**