Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
tags_flavor: suffix=-dev
# As this is a "dev" image, its tags are all suffixed with "-dev". Otherwise, it uses the same
# tagging logic as the primary 'dspace/dspace-angular' image above.
# run_python_version_script: true
# python_version_script_dest: src/static-files/VERSION_D.html
run_python_version_script: true
python_version_script_dest: src/static-files/VERSION_D.html
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
Expand All @@ -52,8 +52,8 @@ jobs:
build_id: dspace-angular
image_name: dataquest/dspace-angular
dockerfile_path: ./Dockerfile.dist
# run_python_version_script: true
# python_version_script_dest: src/static-files/VERSION_D.html
run_python_version_script: true
python_version_script_dest: src/static-files/VERSION_D.html
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
Expand Down
5 changes: 5 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
"aot": true,
"assets": [
"src/assets",
{
"glob": "VERSION_D.html",
"input": "src/static-files",
"output": "static-files"
},
Comment thread
Kasinhou marked this conversation as resolved.
"src/robots.txt"
],
"styles": [
Expand Down
38 changes: 38 additions & 0 deletions scripts/sourceversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import subprocess
import sys
from datetime import datetime, timezone

# when next editing this script, please introduce argparse.
# do not forget, it is called in BE by .github\workflows\reusable-docker-build.yml
# argparse must be introduced there.
# that action also calls BE version of this script, which is different (BE: scripts/sourceversion.py).
# It must also cooperate with argparse

# the idea is, that this will be different on each branch, but could be possibly passed by argv/argparse
RELEASE_TAG_BASE='none'

def get_time_in_timezone(zone: str = "Europe/Bratislava"):
try:
from zoneinfo import ZoneInfo
my_tz = ZoneInfo(zone)
except Exception as e:
my_tz = timezone.utc
return datetime.now(my_tz)


if __name__ == '__main__':
ts = get_time_in_timezone()
# we have html tags, since this script ends up creating VERSION_D.html
print(f"<h4>This info was generated on: <br> <strong> {ts.strftime('%Y-%m-%d %H:%M:%S %Z%z')} </strong> </h4>")

cmd = 'git log -1 --pretty=format:"<h4>Git hash: <br><strong> %H </strong> <br> Date of commit: <br> <strong> %ai </strong></h4>"'
subprocess.check_call(cmd, shell=True)

# when adding argparse, this should be a bit more obvious
link = sys.argv[1] + sys.argv[2]
print('<br> <h4>Build run: </h4> <a href="' + link + '"> ' + link + '</a> ')
Comment thread
Kasinhou marked this conversation as resolved.

link = "https://github.com/dataquest-dev/dspace-angular/releases/tag/" \
+ RELEASE_TAG_BASE + "-" + datetime.now().strftime('%Y.%m.') + sys.argv[2]

print('<br> <br> <h4>Release link: </h4><a href="' + link + '"> ' + link + '</a> (if it does not work, then this is not an official release instance) ')
6 changes: 6 additions & 0 deletions src/app/app-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component';
import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound.component';
import { PROCESS_MODULE_PATH } from './process-page/process-page-routing.paths';
import { STATIC_PAGE_PATH } from './static-page/static-page-routing-paths';
import { viewTrackerResolver } from './statistics/angulartics/dspace/view-tracker.resolver';
import { provideSubmissionState } from './submission/provide-submission-state';
import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths';
Expand Down Expand Up @@ -289,6 +290,11 @@ export const APP_ROUTES: Route[] = [
.then((m) => m.ROUTES),
canActivate: [notAuthenticatedGuard],
},
{
path: STATIC_PAGE_PATH,
loadChildren: () => import('./static-page/static-page-routes')
.then((m) => m.ROUTES),
},
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent, data: { title: '404.page-not-found' } },
],
},
Expand Down
83 changes: 83 additions & 0 deletions src/app/shared/html-content.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { isPlatformBrowser } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import {
Inject,
Injectable,
PLATFORM_ID,
} from '@angular/core';
import {
firstValueFrom,
of,
} from 'rxjs';
import {
catchError,
map,
} from 'rxjs/operators';

import { LocaleService } from '../core/locale/locale.service';
import {
HTML_SUFFIX,
STATIC_FILES_PROJECT_PATH,
} from '../static-page/static-page-routing-paths';

interface HtmlContentResult {
found: boolean;
body: string;
}

/**
* Service for loading static `.html` files stored in the `/static-files` folder.
*/
@Injectable({
providedIn: 'root',
})
export class HtmlContentService {
constructor(
private http: HttpClient,
private localeService: LocaleService,
@Inject(PLATFORM_ID) private platformId: object,
) {}

private withSuffix(name: string): string {
return name.endsWith(HTML_SUFFIX) ? name : name + HTML_SUFFIX;
}

private fetch(url: string) {
return this.http.get(url, { responseType: 'text' }).pipe(
map((body): HtmlContentResult => ({ found: true, body })),
catchError(() => of<HtmlContentResult>({ found: false, body: '' })),
);
}

/**
* Load the html content for a file name, trying the current locale package first
* (`static-files/<lang>/<file>.html`) and falling back to the default package
* (`static-files/<file>.html`). Returns `undefined` when nothing was found.
*
* The files are fetched client-side only; during SSR this resolves to `undefined`
* and the content is loaded after hydration.
*/
async getHtmlContentByPathAndLocale(fileName: string): Promise<string | undefined> {
if (!isPlatformBrowser(this.platformId)) {
return undefined;
}

let language = await firstValueFrom(this.localeService.getCurrentLanguageCode());
// Default language `en` lives in the non-translated (root) package.
language = language === 'en' ? '' : language;

if (language) {
const localized = await firstValueFrom(
this.fetch(this.withSuffix(`${STATIC_FILES_PROJECT_PATH}/${language}/${fileName}`)),
);
if (localized.found) {
return localized.body;
}
}

const fallback = await firstValueFrom(
this.fetch(this.withSuffix(`${STATIC_FILES_PROJECT_PATH}/${fileName}`)),
);
return fallback.found ? fallback.body : undefined;
}
}
22 changes: 22 additions & 0 deletions src/app/shared/utils/clarin-safehtml.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
Pipe,
PipeTransform,
} from '@angular/core';
import {
DomSanitizer,
SafeHtml,
} from '@angular/platform-browser';

/**
* Pipe to keep html tags (e.g. `id`) when rendering a string via `[innerHTML]`.
*/
@Pipe({
name: 'dsSafeHtml',
})
export class ClarinSafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}

transform(htmlString: string): SafeHtml {
return this.sanitized.bypassSecurityTrustHtml(htmlString ?? '');
}
}
Comment thread
Kasinhou marked this conversation as resolved.
10 changes: 10 additions & 0 deletions src/app/static-page/static-page-routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Route } from '@angular/router';

import { StaticPageComponent } from './static-page.component';

export const ROUTES: Route[] = [
{
path: ':id',
component: StaticPageComponent,
},
];
8 changes: 8 additions & 0 deletions src/app/static-page/static-page-routing-paths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Constants for the `/static` route.
*/
export const STATIC_PAGE_PATH = 'static';

export const STATIC_FILES_PROJECT_PATH = 'static-files';

export const HTML_SUFFIX = '.html';
28 changes: 28 additions & 0 deletions src/app/static-page/static-page.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@if (contentState === 'loading') {
<div class="container text-center my-5">
<div class="spinner-border" role="status">
<span class="visually-hidden">{{ 'loading.default' | translate }}</span>
</div>
</div>
}

<!-- Show static page content when found -->
@if (contentState === 'found') {
<div class="container">
<div [innerHTML]="htmlContent | dsSafeHtml"></div>
</div>
}

<!-- Show 404 error when content not found -->
@if (contentState === 'not-found') {
<div class="container page-not-found">
<h1>404</h1>
<h2><small>{{ 'static-page.404.page-not-found' | translate }}</small></h2>
<br/>
<p>{{ 'static-page.404.help' | translate }}</p>
<br/>
<p class="text-center">
<a routerLink="/home" class="btn btn-primary">{{ 'static-page.404.link.home-page' | translate }}</a>
</p>
</div>
}
5 changes: 5 additions & 0 deletions src/app/static-page/static-page.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.page-not-found {
text-align: center;
margin-top: 3rem;
margin-bottom: 3rem;
}
83 changes: 83 additions & 0 deletions src/app/static-page/static-page.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
ChangeDetectorRef,
Component,
OnInit,
} from '@angular/core';
import {
ActivatedRoute,
RouterLink,
} from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';

import { ServerResponseService } from '../core/services/server-response.service';
import { HtmlContentService } from '../shared/html-content.service';
import { ClarinSafeHtmlPipe } from '../shared/utils/clarin-safehtml.pipe';

/**
* Component which load and show static files from the `static-files` folder.
* E.g., `<UI_URL>/static/some_file` loads the content from `static-files/some_file.html`.
*/
Comment thread
Kasinhou marked this conversation as resolved.
@Component({
selector: 'ds-static-page',
templateUrl: './static-page.component.html',
styleUrls: ['./static-page.component.scss'],
imports: [
ClarinSafeHtmlPipe,
RouterLink,
TranslateModule,
],
})
export class StaticPageComponent implements OnInit {
htmlContent = '';
contentState: 'loading' | 'found' | 'not-found' = 'loading';

constructor(
private htmlContentService: HtmlContentService,
private route: ActivatedRoute,
private responseService: ServerResponseService,
private changeDetector: ChangeDetectorRef,
) {}

async ngOnInit(): Promise<void> {
this.contentState = 'loading';
this.htmlContent = '';

const fileName = this.getHtmlFileName();
if (!fileName) {
this.markNotFound();
return;
}

try {
const content = await this.htmlContentService.getHtmlContentByPathAndLocale(fileName);
if (content !== undefined) {
this.htmlContent = content;
this.contentState = 'found';
this.changeDetector.detectChanges();
return;
}
} catch {
// fall through to not-found handling below
}

this.markNotFound();
}
Comment thread
Kasinhou marked this conversation as resolved.

private markNotFound(): void {
this.responseService.setNotFound();
this.contentState = 'not-found';
this.changeDetector.detectChanges();
}

/**
* Read the file name from the URL - `static/FILE_NAME`.
*/
private getHtmlFileName(): string | null {
const id = this.route.snapshot.paramMap.get('id');
if (!id) {
return null;
}
// Drop any trailing fragment, e.g. `VERSION_D#section`.
return id.split('#')[0];
}
}
9 changes: 9 additions & 0 deletions src/assets/i18n/cs.json5
Original file line number Diff line number Diff line change
Expand Up @@ -7532,6 +7532,15 @@
// "sorting.person.birthDate.DESC": "Birth Date Descending",
"sorting.person.birthDate.DESC": "Datum narození sestupně",

// "static-page.404.help": "The static page you requested does not exist. It may have been moved or deleted. You can use the button below to get back to the home page.",
"static-page.404.help": "Požadovaná statická stránka neexistuje. Mohla být přesunuta nebo smazána. Pomocí níže uvedeného tlačítka se můžete vrátit na domovskou stránku.",

// "static-page.404.link.home-page": "Take me to the home page",
"static-page.404.link.home-page": "Návrat na domovskou stránku",

// "static-page.404.page-not-found": "page not found",
"static-page.404.page-not-found": "stránka nebyla nalezena",
Comment thread
Kasinhou marked this conversation as resolved.

// "statistics.title": "Statistics",
"statistics.title": "Statistiky",

Expand Down
7 changes: 7 additions & 0 deletions src/assets/i18n/en.json5
Original file line number Diff line number Diff line change
Expand Up @@ -5012,6 +5012,13 @@

"sorting.person.birthDate.DESC": "Birth Date Descending",

"static-page.404.help": "The static page you requested does not exist. It may have been moved or deleted. You can use the button below to get back to the home page.",

"static-page.404.link.home-page": "Take me to the home page",

"static-page.404.page-not-found": "page not found",
Comment thread
Kasinhou marked this conversation as resolved.


"statistics.title": "Statistics",

"statistics.header": "Statistics for {{ scope }}",
Expand Down
1 change: 1 addition & 0 deletions src/static-files/VERSION_D.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- Regenerated at build time by scripts/sourceversion.py and served at /static/VERSION_D. -->
Loading