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.mail=
|
||||
|
||||
language.path=/tempLang/i18n/
|
||||
language.path=tempLang/i18n/
|
||||
|
||||
userguide.path=tempGuide/
|
|
@ -118,6 +118,14 @@ const appRoutes: Routes = [
|
|||
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',
|
||||
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
|
||||
export function HttpLoaderFactory(http: HttpClient) {
|
||||
return new TranslateServerLoader(http);
|
||||
//GK: In case we want the original translation provider uncomment the line below.
|
||||
//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 { AdminAuthGuard } from './admin-auth-guard.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.
|
||||
|
@ -95,7 +96,8 @@ export class CoreServiceModule {
|
|||
EmailConfirmationService,
|
||||
ContactSupportService,
|
||||
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" href="#"><i class="fa fa-life-ring style-icon"></i>Help</a>
|
||||
</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="col-6 text-center">
|
||||
<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 { takeUntil } from 'rxjs/operators';
|
||||
import { AuthService } from "@app/core/services/auth/auth.service";
|
||||
import { UserGuideDialogComponent } from '@app/ui/user-guide/dialog/user-guide-dialog.component';
|
||||
|
||||
@Component({
|
||||
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 {
|
||||
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 { SidebarFooterComponent } from './sidebar-footer/sidebar-footer.component';
|
||||
import { SidebarComponent } from './sidebar.component';
|
||||
import { UserGuideModule } from '../user-guide/user-guide.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -15,7 +16,8 @@ import { SidebarComponent } from './sidebar.component';
|
|||
GlossaryModule,
|
||||
FaqModule,
|
||||
RouterModule,
|
||||
ContactModule
|
||||
ContactModule,
|
||||
UserGuideModule
|
||||
],
|
||||
declarations: [
|
||||
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": {
|
||||
"CONTACT-SUPPORT": "Contact Support",
|
||||
"FAQ": "FAQ",
|
||||
"GUIDE": "User Guide",
|
||||
"GLOSSARY": "Glossary",
|
||||
"TERMS-OF-SERVICE": "Terms Of Service",
|
||||
"PRIVACY-POLICY": "Privacy Policy"
|
||||
|
@ -1011,6 +1012,10 @@
|
|||
"TITLE-DASHED": "-FAQ-",
|
||||
"CLOSE": "Close"
|
||||
},
|
||||
"GUIDE": {
|
||||
"TITLE": "-User Guide-",
|
||||
"MAIN-CONTENT": ""
|
||||
},
|
||||
"PRIVACY-POLICY": {
|
||||
"TITLE": "-Privacy Policy-",
|
||||
"MAIN-CONTENT": ""
|
||||
|
|
Loading…
Reference in New Issue