diff --git a/jalhyd_branch b/jalhyd_branch index a9175be31026e50a665fd30eb8c49e457c67ed16..d64531f1305e091791eac674c3a36d86b9e17ddd 100644 --- a/jalhyd_branch +++ b/jalhyd_branch @@ -1 +1 @@ -318-mettre-a-jour-les-paquets-npm +devel diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c82cf3c6e5d970ae013aa81f118a2de4418eba62..993c7a33cb544b26503c2b041f4aab7f6f8182cf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -645,7 +645,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { }); } - public async loadSessionFile(f: File, info?: any) { + public async loadSessionFile(f: File|string, info?: any) { // notes merge detection: was there already some notes ? const existingNotes = Session.getInstance().documentation; // load diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d81af7b4eead0944956b55271706584020ec28f4..468ee73c90db11b7388b49f6d0b2adc719a8fe72 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -117,6 +117,9 @@ import { JalhydModelValidationStepDirective } from "./directives/jalhyd-model-validation.directive"; import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher"; +import { LoadSessionURLComponent } from "./components/load-session-url/load-session-url.component"; +import { DialogShowMessageComponent } from "./components/dialog-show-message/dialog-show-message.component"; +import { DialogConfirmLoadSessionURLComponent } from "./components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component"; const appRoutes: Routes = [ { path: "list/search", component: CalculatorListComponent }, @@ -125,6 +128,7 @@ const appRoutes: Routes = [ { path: "setup", component: ApplicationSetupComponent }, { path: "diagram", component: ModulesDiagramComponent }, { path: "properties", component: SessionPropertiesComponent }, + { path: "loadsession/:path", component: LoadSessionURLComponent }, { path: "**", redirectTo: "list", pathMatch: "full" } ]; @@ -201,6 +205,8 @@ const appRoutes: Routes = [ DialogSaveSessionComponent, DialogNewPbCloisonComponent, DialogLoadPredefinedEspeceComponent, + DialogShowMessageComponent, + DialogConfirmLoadSessionURLComponent, FieldSetComponent, FieldsetContainerComponent, FixedResultsComponent, @@ -217,6 +223,7 @@ const appRoutes: Routes = [ JalhydModelValidationStepDirective, JetResultsComponent, JetTrajectoryChartComponent, + LoadSessionURLComponent, LogComponent, LogDrawerComponent, LogEntryComponent, @@ -247,7 +254,7 @@ const appRoutes: Routes = [ VarResultsComponent, VerificateurResultsComponent ], - providers: [ + providers: [ // services ApplicationSetupService, CustomBreakPointsProvider, FormulaireService, diff --git a/src/app/components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component.html b/src/app/components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component.html new file mode 100644 index 0000000000000000000000000000000000000000..749838ad4469a9c976d24ed31e71ba884effa803 --- /dev/null +++ b/src/app/components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component.html @@ -0,0 +1,15 @@ +<h1 mat-dialog-title [innerHTML]="uitextTitle"></h1> +<div> + <mat-checkbox [(ngModel)]="emptyCurrentSession"> + {{ uitextEmptyCurrentSession }} + </mat-checkbox> + + <div mat-dialog-actions [attr.align]="'end'"> + <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial> + {{ uitextCancel }} + </button> + <button mat-raised-button type="submit" color="warn" (click)="loadSession()"> + {{ uitextLoad }} + </button> + </div> +</div> \ No newline at end of file diff --git a/src/app/components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component.ts b/src/app/components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6e0497f9fe2f40f30ce95bea24bd65c15b4bc52 --- /dev/null +++ b/src/app/components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component.ts @@ -0,0 +1,39 @@ +import { Component, Inject } from "@angular/core"; +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; + +@Component({ + selector: "dialog-confirm-load-session-url", + templateUrl: "dialog-confirm-load-session-url.component.html", +}) +export class DialogConfirmLoadSessionURLComponent { + + public emptyCurrentSession: boolean = false; + + constructor( + public dialogRef: MatDialogRef<DialogConfirmLoadSessionURLComponent>, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + } + + public loadSession() { + this.dialogRef.close({ + emptySession: this.emptyCurrentSession + }); + } + + public get uitextTitle() { + return "Please confirm loading"; + } + + public get uitextEmptyCurrentSession() { + return "Empty current session"; + } + + public get uitextCancel() { + return "Cancel"; + } + + public get uitextLoad() { + return "Load"; + } +} diff --git a/src/app/components/dialog-show-message/dialog-show-message.component.html b/src/app/components/dialog-show-message/dialog-show-message.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9ed2144e31f563bd6f224ea9a7fac70ecdbf8202 --- /dev/null +++ b/src/app/components/dialog-show-message/dialog-show-message.component.html @@ -0,0 +1,10 @@ +<h1 mat-dialog-title [innerHTML]="uitextTitle"></h1> +<div mat-dialog-content> + {{ uitextMessage }} +</div> + +<div mat-dialog-actions [attr.align]="'end'"> + <button mat-raised-button color="warn" [mat-dialog-close]="true"> + {{ uitextClose }} + </button> +</div> \ No newline at end of file diff --git a/src/app/components/dialog-show-message/dialog-show-message.component.scss b/src/app/components/dialog-show-message/dialog-show-message.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/components/dialog-show-message/dialog-show-message.component.ts b/src/app/components/dialog-show-message/dialog-show-message.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..67a3740fb5e190ead2c59a90575a57ddca101fda --- /dev/null +++ b/src/app/components/dialog-show-message/dialog-show-message.component.ts @@ -0,0 +1,34 @@ +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { Inject, Component } from "@angular/core"; + +@Component({ + selector: "dialog-show-message", + templateUrl: "dialog-show-message.component.html", + styleUrls: ["dialog-show-message.component.scss"] +}) +export class DialogShowMessageComponent { + + private title: string; + + private message: string; + + constructor( + public dialogRef: MatDialogRef<DialogShowMessageComponent>, + @Inject(MAT_DIALOG_DATA) data: any + ) { + this.title = data.title; + this.message = data.message; + } + + public get uitextTitle() { + return this.title; + } + + public get uitextMessage() { + return this.message; + } + + public get uitextClose() { + return "Close"; + } +} diff --git a/src/app/components/load-session-url/load-session-url.component.ts b/src/app/components/load-session-url/load-session-url.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6409e8142ca7d4887a3c9fc0dbaae38e633fc8ed --- /dev/null +++ b/src/app/components/load-session-url/load-session-url.component.ts @@ -0,0 +1,136 @@ +import { Location } from "@angular/common"; +import { Component, forwardRef, Inject } from "@angular/core"; +import { MatDialog } from "@angular/material/dialog"; +import { ActivatedRoute } from "@angular/router"; +import { AppComponent } from "app/app.component"; +import { FormulaireService } from "app/services/formulaire.service"; +import { HttpService } from "app/services/http.service"; +import { DialogConfirmLoadSessionURLComponent } from "../dialog-confirm-load-session-url/dialog-confirm-load-session-url.component"; +import { DialogShowMessageComponent } from "../dialog-show-message/dialog-show-message.component"; + +// load a session file by its URL (either local or remote) + +@Component({ + selector: "load-session-url", + template: "" +}) +export class LoadSessionURLComponent { + + constructor( + @Inject(forwardRef(() => AppComponent)) private appComponent: AppComponent, + private dialog: MatDialog, + private route: ActivatedRoute, + private httpService: HttpService, + private location: Location, + private formulaireService: FormulaireService + ) { + } + + ngOnInit() { + // check open calculators + if (this.formulaireService.formulaires.length > 0) { + this.confirmLoadSession().then(emptySession => { + if (emptySession === undefined) { + // cancel has been clicked, go to previous route + this.location.back(); + } + else { + if (emptySession) { + this.appComponent.doEmptySession(); + } + this.loadSession(); + } + }); + } + else { + this.loadSession(); + } + } + + private loadSession() { + // get "path" argument from URL + const path = this.route.snapshot.params.path; + + if (path.startsWith("http")) { + // general URL path + + // example URLs: + // http://localhost:4200/#/loadsession/https%3A%2F%2Fhydraulique.g-eau.fr%2Fcassiopee%2Fdevel%2Fapp%2Fexamples%2Fpab-complete-chain.json + // http://localhost/dist/#/loadsession/https%3A%2F%2Fhydraulique.g-eau.fr%2Fcassiopee%2Fdevel%2Fapp%2Fexamples%2Fpab-complete-chain.json + // turned by loadRemoteSession() to + // http://localhost/gofetch.php?url=https%3A%2F%2Fhydraulique.g-eau.fr%2Fcassiopee%2Fdevel%2Fapp%2F%2Fexamples%2Fpab-complete-chain.json + this.loadRemoteSession(path); + } + else { + // local path + + // input URL example : http://localhost:4200/#/loadsession/app%2Fexamples%2Fpab-complete-chain.json + // extracted path : app/examples/pab-complete-chain.json + + this.loadLocalSession(path); + } + } + + private async loadRemoteSession(path: string) { + try { + const url = "assets/scripts/gofetch.php?url=" + encodeURIComponent(path); + this.httpService.httpGetRequestPromise(url).then(resp => { + const s = JSON.stringify(resp); + this.appComponent.loadSessionFile(s); + }); + } catch (e) { + // display error dialog + await this.openErrorDialog(path); + // go to previous route + this.location.back(); + } + } + + /** + * load a locally stored session file + * @param path local path in the form eg. app/examples/pab-complete-chain.json + */ + private async loadLocalSession(path: string) { + try { + const d = await this.httpService.httpGetBlobRequestPromise(path); + const f: any = new Blob([d], { type: "application/json" }); + this.appComponent.loadSessionFile(f); + } catch (e) { + // display error dialog + await this.openErrorDialog(path); + // go to previous route + this.location.back(); + } + } + + private async openErrorDialog(path: string) { + const dialogRef = this.dialog.open( + DialogShowMessageComponent, + { + data: { + title: "Error!", + message: "Session " + path + " does not exist." + }, + disableClose: true + } + ); + + // wait for dialog to be closed + await dialogRef.afterClosed().toPromise(); + } + + private confirmLoadSession(): Promise<boolean> { + const dialogRef = this.dialog.open( + DialogConfirmLoadSessionURLComponent, + { + data: { + }, + disableClose: true + } + ); + + return dialogRef.afterClosed().toPromise().then(result => { + return result.emptySession; + }); + } +} diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts index bf650c62bc19222062bfa134e44811f99af837cb..775e6ae0e1cc45f5c442857705dbc88d0f3b3c89 100644 --- a/src/app/services/formulaire.service.ts +++ b/src/app/services/formulaire.service.ts @@ -628,13 +628,18 @@ export class FormulaireService extends Observable { * @param f fichier session * @param formInfos infos sur les modules de calcul @see DialogLoadSessionComponent.calculators */ - public async loadSession(f: File, formInfos: any[] = []): Promise<{ hasErrors: boolean, loaded: string[] }> { + public async loadSession(i: Blob | string, formInfos: any[] = []): Promise<{ hasErrors: boolean, loaded: string[] }> { try { // disable "empty fields" flag temporarly const emptyFields = ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit; ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit = false; - const s = await this.readSingleFile(f); + let s; + if (i instanceof Blob) { + s = await this.readSingleFile(i as File); + } else { + s = i; + } const uids: string[] = []; formInfos.forEach((fi) => { if (fi.selected) { @@ -827,7 +832,7 @@ export class FormulaireService extends Observable { ) { const dependingNubs = Session.getInstance().getDependingNubs(f.currentNub.uid, symbol, forceResetAllDependencies, true); for (const dn of dependingNubs) { - if (! visited.includes(dn.uid)) { + if (!visited.includes(dn.uid)) { const form = this.getFormulaireFromNubId(dn.uid); if (form) { const hadResults = form.hasResults; diff --git a/src/assets/scripts/gofetch.php b/src/assets/scripts/gofetch.php new file mode 100755 index 0000000000000000000000000000000000000000..84610857289eb40822ae4d7beace37f57ee33322 --- /dev/null +++ b/src/assets/scripts/gofetch.php @@ -0,0 +1,57 @@ +<?php + +function do_log($msg) { + // echo($msg."\n"); +} + +do_log("start"); + +// http://localhost/gofetch.php?url=http%3A%2F%2Flocalhost%3A4200%2Fapp%2F%2Fexamples%2Fperr.json +// http://localhost/gofetch.php?url=https%3A%2F%2Fhydraulique.g-eau.fr%2Fcassiopee%2Fdevel%2Fapp%2F%2Fexamples%2Fperr.json + +// fonction str_ends_with si PHP < 8 +if ( ! function_exists( 'str_ends_with' ) ) { + function str_ends_with( $haystack, $needle ) { + if ( '' === $haystack && '' !== $needle ) { + return false; + } + $len = strlen( $needle ); + return 0 === substr_compare( $haystack, $needle, -$len, $len ); + } +} + +do_log("get url"); +$url = $_GET['url']; +do_log("url=" . $url); + +$url=urldecode($url); +do_log("decode url=" . $url); + +if( str_ends_with( strtolower( $url ), '.json' ) ) { + +do_log("curl init"); +// Initialise une session CURL. +$ch = curl_init(); + +do_log("setopt 1"); +// Récupère le contenu de la page +curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + +do_log("setopt 2"); +// Configure l'URL +curl_setopt($ch, CURLOPT_URL, $url); + +// Désactive la vérification du certificat si l'URL utilise HTTPS +if (strpos($url,'https')===0) { + do_log("setopt 3"); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); +} + +do_log("curl exec"); +// Exécute la requête +$result = curl_exec($ch); + +// Affiche le résultat +echo $result; +} +?>