Add user guide on the sidebar footer (ref #239)
This commit is contained in:
parent
68908d3d77
commit
85689db9aa
|
@ -0,0 +1,56 @@
|
||||||
|
package eu.eudat.controllers;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin
|
||||||
|
@RequestMapping(value = {"/api/userguide/"})
|
||||||
|
public class UserGuideController {
|
||||||
|
|
||||||
|
private Environment environment;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UserGuideController(Environment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(path = "current", method = RequestMethod.GET )
|
||||||
|
public ResponseEntity getUserGuide() throws IOException {
|
||||||
|
Stream<Path> walk = Files.walk(Paths.get(this.environment.getProperty("userguide.path")));
|
||||||
|
List<String> result = walk.filter(Files::isRegularFile)
|
||||||
|
.map(Path::toString).collect(Collectors.toList());
|
||||||
|
|
||||||
|
String fileName = result.get(0);
|
||||||
|
InputStream is = new FileInputStream(fileName);
|
||||||
|
|
||||||
|
HttpHeaders responseHeaders = new HttpHeaders();
|
||||||
|
responseHeaders.setContentLength(is.available());
|
||||||
|
responseHeaders.setContentType(MediaType.TEXT_HTML);
|
||||||
|
responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName);
|
||||||
|
responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
|
||||||
|
responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
|
||||||
|
|
||||||
|
byte[] content = new byte[is.available()];
|
||||||
|
is.read(content);
|
||||||
|
is.close();
|
||||||
|
|
||||||
|
return new ResponseEntity<>(content, responseHeaders, HttpStatus.OK);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,4 +68,6 @@ zenodo.access_token=
|
||||||
#############CONTACT EMAIL CONFIGURATIONS#########
|
#############CONTACT EMAIL CONFIGURATIONS#########
|
||||||
contact_email.mail=
|
contact_email.mail=
|
||||||
|
|
||||||
language.path=/tempLang/i18n/
|
language.path=tempLang/i18n/
|
||||||
|
|
||||||
|
userguide.path=tempGuide/
|
|
@ -118,6 +118,14 @@ const appRoutes: Routes = [
|
||||||
title: 'FAQ.TITLE'
|
title: 'FAQ.TITLE'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'user-guide',
|
||||||
|
loadChildren: () => import('./ui/user-guide/user-guide.module').then(m => m.UserGuideModule),
|
||||||
|
data: {
|
||||||
|
breadcrumb: true,
|
||||||
|
title: 'GUIDE.TITLE'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'privacy-policy',
|
path: 'privacy-policy',
|
||||||
loadChildren: () => import('./ui/sidebar/sidebar-footer/privacy/privacy.module').then(m => m.PrivacyModule),
|
loadChildren: () => import('./ui/sidebar/sidebar-footer/privacy/privacy.module').then(m => m.PrivacyModule),
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { BaseHttpService } from './core/services/http/base-http.service';
|
||||||
// AoT requires an exported function for factories
|
// AoT requires an exported function for factories
|
||||||
export function HttpLoaderFactory(http: HttpClient) {
|
export function HttpLoaderFactory(http: HttpClient) {
|
||||||
return new TranslateServerLoader(http);
|
return new TranslateServerLoader(http);
|
||||||
|
//GK: In case we want the original translation provider uncomment the line below.
|
||||||
//return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
|
//return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import { ContactSupportService } from './services/contact-support/contact-suppor
|
||||||
import { LanguageService } from './services/language/language.service';
|
import { LanguageService } from './services/language/language.service';
|
||||||
import { AdminAuthGuard } from './admin-auth-guard.service';
|
import { AdminAuthGuard } from './admin-auth-guard.service';
|
||||||
import { LockService } from './services/lock/lock.service';
|
import { LockService } from './services/lock/lock.service';
|
||||||
|
import { UserGuideService } from './services/user-guide/user-guide.service';
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// This is shared module that provides all the services. Its imported only once on the AppModule.
|
// This is shared module that provides all the services. Its imported only once on the AppModule.
|
||||||
|
@ -95,7 +96,8 @@ export class CoreServiceModule {
|
||||||
EmailConfirmationService,
|
EmailConfirmationService,
|
||||||
ContactSupportService,
|
ContactSupportService,
|
||||||
LanguageService,
|
LanguageService,
|
||||||
LockService
|
LockService,
|
||||||
|
UserGuideService
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { environment } from 'environments/environment';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { HttpResponse, HttpClient } from '@angular/common/http';
|
||||||
|
import { BaseHttpService } from '../http/base-http.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserGuideService {
|
||||||
|
private userGuideUrl = `${environment.Server}userguide`;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private translate: TranslateService,
|
||||||
|
private http: HttpClient,
|
||||||
|
private baseHttp: BaseHttpService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public getUserGuide(): Observable<HttpResponse<Blob>> {
|
||||||
|
return this.http.get(`${this.userGuideUrl}/current`, { responseType: 'blob', observe: 'response', headers: {'Content-type': 'text/html',
|
||||||
|
'Accept': 'text/html',
|
||||||
|
'Access-Control-Allow-Origin': environment.App,
|
||||||
|
'Access-Control-Allow-Credentials': 'true'} });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,13 @@
|
||||||
<a class="option vl" href="#"><i class="fa fa-book style-icon"></i>Guide</a>
|
<a class="option vl" href="#"><i class="fa fa-book style-icon"></i>Guide</a>
|
||||||
<a class="option" href="#"><i class="fa fa-life-ring style-icon"></i>Help</a>
|
<a class="option" href="#"><i class="fa fa-life-ring style-icon"></i>Help</a>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
<div class="row d-flex flex-reverse">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<p class="option" (click)="openUserGuideDialog()" [ngClass]="{'option-active': this.router.url === '/user-guide'}">
|
||||||
|
<!-- <i class="fa fa-question-circle pr-2 pt-1"></i> -->
|
||||||
|
{{'FOOTER.GUIDE' | translate}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row d-flex flex-reverse">
|
<div class="row d-flex flex-reverse">
|
||||||
<div class="col-6 text-center">
|
<div class="col-6 text-center">
|
||||||
<p class="option" (click)="openGlossaryDialog()" [ngClass]="{'option-active': this.router.url === '/glossary'}">
|
<p class="option" (click)="openGlossaryDialog()" [ngClass]="{'option-active': this.router.url === '/glossary'}">
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { ValidationErrorModel } from '@common/forms/validation/error-model/valid
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { AuthService } from "@app/core/services/auth/auth.service";
|
import { AuthService } from "@app/core/services/auth/auth.service";
|
||||||
|
import { UserGuideDialogComponent } from '@app/ui/user-guide/dialog/user-guide-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sidebar-footer',
|
selector: 'app-sidebar-footer',
|
||||||
|
@ -97,6 +98,20 @@ export class SidebarFooterComponent extends BaseComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openUserGuideDialog() {
|
||||||
|
if (this.dialog.openDialogs.length > 0) {
|
||||||
|
this.dialog.closeAll();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const dialogRef = this.dialog.open(UserGuideDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
data: {
|
||||||
|
isDialog: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onCallbackSuccess(): void {
|
onCallbackSuccess(): void {
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-EMAIL-SEND'), SnackBarNotificationLevel.Success);
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-EMAIL-SEND'), SnackBarNotificationLevel.Success);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { FaqModule } from '../faq/faq.module';
|
||||||
import { GlossaryModule } from '../glossary/glossary.module';
|
import { GlossaryModule } from '../glossary/glossary.module';
|
||||||
import { SidebarFooterComponent } from './sidebar-footer/sidebar-footer.component';
|
import { SidebarFooterComponent } from './sidebar-footer/sidebar-footer.component';
|
||||||
import { SidebarComponent } from './sidebar.component';
|
import { SidebarComponent } from './sidebar.component';
|
||||||
|
import { UserGuideModule } from '../user-guide/user-guide.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -15,7 +16,8 @@ import { SidebarComponent } from './sidebar.component';
|
||||||
GlossaryModule,
|
GlossaryModule,
|
||||||
FaqModule,
|
FaqModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
ContactModule
|
ContactModule,
|
||||||
|
UserGuideModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
SidebarComponent,
|
SidebarComponent,
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<div class="faq">
|
||||||
|
<div class="row d-flex flex-row">
|
||||||
|
<div mat-dialog-title class="col-auto">
|
||||||
|
{{'GUIDE.TITLE' | translate}}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto ml-auto close-btn" (click)="close()">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-content class="row">
|
||||||
|
<app-user-guide-content></app-user-guide-content>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="row">
|
||||||
|
<div class="ml-auto col-auto"><button mat-button mat-dialog-close mat-raised-button color="primary" (click)="cancel()">{{'FAQ.CLOSE' | translate}}</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,17 @@
|
||||||
|
.form {
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq {
|
||||||
|
margin-bottom: 1.125rem;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-user-guide-dialog',
|
||||||
|
templateUrl: './user-guide-dialog.component.html',
|
||||||
|
styleUrls: ['./user-guide-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class UserGuideDialogComponent {
|
||||||
|
|
||||||
|
public isDialog: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<UserGuideDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
|
) {
|
||||||
|
this.isDialog = data.isDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
send() {
|
||||||
|
this.dialogRef.close(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.dialogRef.close(false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<div id='userguide' [innerHTML]="guideHTML"></div>
|
|
@ -0,0 +1,7 @@
|
||||||
|
:host ::ng-deep .href{
|
||||||
|
color: lightblue !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host :hover ::ng-deep .href {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Component, OnInit, AfterViewChecked } from '@angular/core';
|
||||||
|
import { UserGuideService } from '@app/core/services/user-guide/user-guide.service';
|
||||||
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
|
import { takeUntil } from 'rxjs/internal/operators/takeUntil';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-user-guide-content',
|
||||||
|
templateUrl: './user-guide-content.component.html',
|
||||||
|
styleUrls: ['./user-guide-content.component.scss']
|
||||||
|
})
|
||||||
|
export class UserGuideContentComponent extends BaseComponent implements OnInit, AfterViewChecked {
|
||||||
|
|
||||||
|
|
||||||
|
guideHTML: any;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private userGuideService: UserGuideService,
|
||||||
|
private sanitizer: DomSanitizer
|
||||||
|
) { super(); }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
|
||||||
|
this.userGuideService.getUserGuide()
|
||||||
|
.pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(response => {
|
||||||
|
const blob = new Blob([response.body], { type: 'text/html' });
|
||||||
|
this.readBlob(blob);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewChecked(): void {
|
||||||
|
this.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
readBlob(blob: Blob) {
|
||||||
|
const fr = new FileReader();
|
||||||
|
fr.onload = ev => {
|
||||||
|
this.guideHTML = this.sanitizer.bypassSecurityTrustHtml(fr.result as string);
|
||||||
|
this.parse();
|
||||||
|
};
|
||||||
|
fr.readAsText(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
scroll(ev: Event) {
|
||||||
|
document.getElementById((ev.srcElement as any).getAttribute('path')).scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private parse() {
|
||||||
|
const specialElements: HTMLCollection = document.getElementsByClassName('href');
|
||||||
|
for (let i = 0; i < specialElements.length; i++) {
|
||||||
|
const element = specialElements.item(i);
|
||||||
|
element.addEventListener('click',(ev) => this.scroll(ev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { UserGuideRoutingModule } from './user-guide.routing';
|
||||||
|
import { UserGuideContentComponent } from './user-guide-content/user-guide-content.component';
|
||||||
|
import { UserGuideDialogComponent } from './dialog/user-guide-dialog.component';
|
||||||
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
UserGuideContentComponent,
|
||||||
|
UserGuideDialogComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonUiModule,
|
||||||
|
UserGuideRoutingModule
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
UserGuideContentComponent,
|
||||||
|
UserGuideDialogComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
UserGuideDialogComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class UserGuideModule { }
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
import { UserGuideContentComponent } from './user-guide-content/user-guide-content.component';
|
||||||
|
import { UserGuideDialogComponent } from './dialog/user-guide-dialog.component';
|
||||||
|
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: UserGuideDialogComponent,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class UserGuideRoutingModule { }
|
|
@ -997,6 +997,7 @@
|
||||||
"FOOTER": {
|
"FOOTER": {
|
||||||
"CONTACT-SUPPORT": "Contact Support",
|
"CONTACT-SUPPORT": "Contact Support",
|
||||||
"FAQ": "FAQ",
|
"FAQ": "FAQ",
|
||||||
|
"GUIDE": "User Guide",
|
||||||
"GLOSSARY": "Glossary",
|
"GLOSSARY": "Glossary",
|
||||||
"TERMS-OF-SERVICE": "Terms Of Service",
|
"TERMS-OF-SERVICE": "Terms Of Service",
|
||||||
"PRIVACY-POLICY": "Privacy Policy"
|
"PRIVACY-POLICY": "Privacy Policy"
|
||||||
|
@ -1011,6 +1012,10 @@
|
||||||
"TITLE-DASHED": "-FAQ-",
|
"TITLE-DASHED": "-FAQ-",
|
||||||
"CLOSE": "Close"
|
"CLOSE": "Close"
|
||||||
},
|
},
|
||||||
|
"GUIDE": {
|
||||||
|
"TITLE": "-User Guide-",
|
||||||
|
"MAIN-CONTENT": ""
|
||||||
|
},
|
||||||
"PRIVACY-POLICY": {
|
"PRIVACY-POLICY": {
|
||||||
"TITLE": "-Privacy Policy-",
|
"TITLE": "-Privacy Policy-",
|
||||||
"MAIN-CONTENT": ""
|
"MAIN-CONTENT": ""
|
||||||
|
|
Loading…
Reference in New Issue