From 0df48da0b4d81f356b7d31c477ac442aa1e2908b Mon Sep 17 00:00:00 2001 From: Martin Mechtel <mechtelm@user.hu-berlin.de> Date: Sun, 30 Sep 2018 22:52:23 +0200 Subject: [PATCH] navigation and reload seems ok --- src/app/app.module.ts | 4 +- src/app/backend.service.ts | 48 +- src/app/errormsg/errormsg.component.css | 3 + src/app/errormsg/errormsg.component.html | 3 + src/app/errormsg/errormsg.component.spec.ts | 25 + src/app/errormsg/errormsg.component.ts | 21 + src/app/logindata.service.ts | 10 +- src/app/start/start.component.html | 2 +- src/app/start/start.component.ts | 32 +- src/app/test-controller/backend.service.ts | 143 +----- .../tc-menu-buttons.component.ts | 7 +- .../tc-sidenavi-button.component.css | 18 + .../tc-sidenavi-button.component.html | 8 + .../tc-sidenavi-button.component.spec.ts | 25 + .../tc-sidenavi-button.component.ts | 33 ++ .../test-controller.component.html | 5 +- .../test-controller.component.ts | 67 +-- .../test-controller/test-controller.module.ts | 4 +- .../test-controller.service.ts | 73 ++- .../test-controller/testdata.service.spec.ts | 15 - src/app/test-controller/testdata.service.ts | 463 ------------------ .../test-controller/unithost/unit-routing.ts | 13 +- .../unithost/unithost.component.html | 2 +- .../unithost/unithost.component.ts | 29 +- src/environments/environment.ts | 2 +- 25 files changed, 351 insertions(+), 704 deletions(-) create mode 100644 src/app/errormsg/errormsg.component.css create mode 100644 src/app/errormsg/errormsg.component.html create mode 100644 src/app/errormsg/errormsg.component.spec.ts create mode 100644 src/app/errormsg/errormsg.component.ts create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts delete mode 100644 src/app/test-controller/testdata.service.spec.ts delete mode 100644 src/app/test-controller/testdata.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8ad2a077..338dc0c0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,12 +16,14 @@ import { BackendService } from './backend.service'; import { StartComponent } from './start/start.component'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; +import { ErrormsgComponent } from './errormsg/errormsg.component'; @NgModule({ declarations: [ AppComponent, StartComponent, - AboutComponent + AboutComponent, + ErrormsgComponent ], imports: [ BrowserModule, diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts index 5d8d69c3..8ac0b3e6 100644 --- a/src/app/backend.service.ts +++ b/src/app/backend.service.ts @@ -24,7 +24,7 @@ export class BackendService { return this.http .post<string>(this.serverUrl + 'testlogin.php', {n: name, p: password}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -39,7 +39,7 @@ export class BackendService { return this.http .post<LoginData>(this.serverUrl + 'getLoginDataByLoginToken.php', {lt: logintoken}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -53,7 +53,7 @@ export class BackendService { return this.http .post<LoginData>(this.serverUrl + 'getLoginDataByPersonToken.php', {pt: persontoken}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -68,7 +68,7 @@ export class BackendService { .post<BookletStatus>(this.serverUrl + 'getBookletStatusByNameAndPersonToken.php', { pt: persontoken, b: bookletname}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -84,7 +84,7 @@ export class BackendService { .post<BookletStatus>(this.serverUrl + 'getBookletStatusByNameAndLoginToken.php', { lt: logintoken, b: bookletid, c: code, bl: bookletlabel}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -99,7 +99,7 @@ export class BackendService { .post<BookletStatus>(this.serverUrl + 'getBookletStatusByDbId.php', { pt: persontoken, b: bookletid}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -114,7 +114,7 @@ export class BackendService { .post<PersonTokenAndBookletId>(this.serverUrl + 'startBookletByLoginToken.php', {lt: logintoken, c: code, b: bookletFilename}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -129,7 +129,7 @@ export class BackendService { return this.http .post<number>(this.serverUrl + 'startBookletByPersonToken.php', {pt: persontoken, b: bookletFilename}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -143,23 +143,25 @@ export class BackendService { return this.http .post<boolean>(this.serverUrl + 'endBooklet.php', {pt: persontoken, b: bookletDbId}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +} - private handleError(errorObj: HttpErrorResponse): Observable<ServerError> { - const myreturn = new ServerError(errorObj.status, 'Fehler bei Datenübertragung'); - - if (errorObj.status === 401) { - myreturn.label = 'Fehler: Zugriff verweigert - bitte (neu) anmelden!'; - } else if (errorObj.status === 503) { - myreturn.label = 'Fehler: Server meldet Datenbankproblem.'; - } else if (errorObj.error instanceof ErrorEvent) { - myreturn.label = 'Fehler: ' + (<ErrorEvent>errorObj.error).message; +export class ErrorHandler { + public static handle(errorObj: HttpErrorResponse): Observable<ServerError> { + let myreturn: ServerError = null; + if (errorObj.error instanceof ErrorEvent) { + myreturn = new ServerError(500, 'Verbindungsproblem', (<ErrorEvent>errorObj.error).message); } else { - myreturn.label = 'Fehler: ' + errorObj.message; + myreturn = new ServerError(errorObj.status, 'Verbindungsproblem', errorObj.message); + if (errorObj.status === 401) { + myreturn.labelNice = 'Zugriff verweigert - bitte (neu) anmelden!'; + } else if (errorObj.status === 503) { + myreturn.labelNice = 'Achtung: Server meldet Datenbankproblem.'; + } } return of(myreturn); @@ -172,10 +174,12 @@ export class BackendService { // class instead of interface to be able to use instanceof to check type export class ServerError { public code: number; - public label: string; - constructor(code: number, label: string) { + public labelNice: string; + public labelSystem: string; + constructor(code: number, labelNice: string, labelSystem) { this.code = code; - this.label = label; + this.labelNice = labelNice; + this.labelSystem = labelSystem; } } diff --git a/src/app/errormsg/errormsg.component.css b/src/app/errormsg/errormsg.component.css new file mode 100644 index 00000000..361c51c8 --- /dev/null +++ b/src/app/errormsg/errormsg.component.css @@ -0,0 +1,3 @@ +div { + color: brown; +} diff --git a/src/app/errormsg/errormsg.component.html b/src/app/errormsg/errormsg.component.html new file mode 100644 index 00000000..8181a966 --- /dev/null +++ b/src/app/errormsg/errormsg.component.html @@ -0,0 +1,3 @@ +<div *ngIf="errorMsg !== null" [matTooltip]="errorMsg.labelSystem"> + {{ errorMsg.labelNice }} +</div> diff --git a/src/app/errormsg/errormsg.component.spec.ts b/src/app/errormsg/errormsg.component.spec.ts new file mode 100644 index 00000000..671f6575 --- /dev/null +++ b/src/app/errormsg/errormsg.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ErrormsgComponent } from './errormsg.component'; + +describe('ErrormsgComponent', () => { + let component: ErrormsgComponent; + let fixture: ComponentFixture<ErrormsgComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ErrormsgComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ErrormsgComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/errormsg/errormsg.component.ts b/src/app/errormsg/errormsg.component.ts new file mode 100644 index 00000000..a12d8f15 --- /dev/null +++ b/src/app/errormsg/errormsg.component.ts @@ -0,0 +1,21 @@ +import { ServerError } from './../backend.service'; +import { LogindataService } from './../logindata.service'; +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-errormsg', + templateUrl: './errormsg.component.html', + styleUrls: ['./errormsg.component.css'] +}) +export class ErrormsgComponent implements OnInit { + private errorMsg: ServerError = null; + + constructor( + private lds: LogindataService + ) { } + + ngOnInit() { + this.lds.globalErrorMsg$.subscribe(m => this.errorMsg = m); + } + +} diff --git a/src/app/logindata.service.ts b/src/app/logindata.service.ts index 9f611044..3503d254 100644 --- a/src/app/logindata.service.ts +++ b/src/app/logindata.service.ts @@ -24,7 +24,7 @@ export class LogindataService { public groupName$ = new BehaviorSubject<string>(''); public personCode$ = new BehaviorSubject<string>(''); public bookletLabel$ = new BehaviorSubject<string>(''); - public globalErrorMsg$ = new BehaviorSubject<string>(''); + public globalErrorMsg$ = new BehaviorSubject<ServerError>(null); public bookletsByCode$ = new BehaviorSubject<BookletDataListByCode>(null); public bookletData$ = new BehaviorSubject<BookletData[]>([]); public loginToken$ = new BehaviorSubject<string>(''); @@ -102,12 +102,12 @@ export class LogindataService { this.bs.getLoginDataByPersonToken(pt).subscribe(loginDataUntyped => { if (loginDataUntyped instanceof ServerError) { const e = loginDataUntyped as ServerError; - this.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.globalErrorMsg$.next(e); this.bookletDbId$.next(0); this.personToken$.next(''); } else { const loginData = loginDataUntyped as LoginData; - this.globalErrorMsg$.next(''); + this.globalErrorMsg$.next(null); this.groupName$.next(loginData.groupname); this.workspaceName$.next(loginData.workspaceName); this.personCode$.next(loginData.code); @@ -120,11 +120,11 @@ export class LogindataService { this.bs.getBookletStatusByDbId(pt, b).subscribe(bookletStatusUntyped => { if (bookletStatusUntyped instanceof ServerError) { const e = bookletStatusUntyped as ServerError; - this.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.globalErrorMsg$.next(e); this.bookletDbId$.next(0); } else { const bookletStatus = bookletStatusUntyped as BookletStatus; - this.globalErrorMsg$.next(''); + this.globalErrorMsg$.next(null); if (bookletStatus.canStart) { this.bookletLabel$.next(bookletStatus.label); } else { diff --git a/src/app/start/start.component.html b/src/app/start/start.component.html index d0c796d2..ab8cf40e 100644 --- a/src/app/start/start.component.html +++ b/src/app/start/start.component.html @@ -95,7 +95,7 @@ <button mat-raised-button color="foreground" *ngIf="validCodes.length > 1" (click)="changeCode()">Personen-Code ändern</button> <p *ngIf="!showLoginForm || validCodes.length > 1"> </p> <button mat-raised-button color="foreground" [routerLink]="['/about']">Impressum/Datenschutz</button> - <p> {{ errorMsg }}</p> + <app-errormsg></app-errormsg> </div> </div> </div> diff --git a/src/app/start/start.component.ts b/src/app/start/start.component.ts index 5ebfe065..f3c58e4d 100644 --- a/src/app/start/start.component.ts +++ b/src/app/start/start.component.ts @@ -27,7 +27,6 @@ export class StartComponent implements OnInit { private testtakerloginform: FormGroup; private codeinputform: FormGroup; - private errorMsg = ''; private testEndButtonText = 'Test beenden'; // ?? @@ -79,8 +78,6 @@ export class StartComponent implements OnInit { } }); - this.lds.globalErrorMsg$.subscribe(m => this.errorMsg = m); - this.testtakerloginform = this.fb.group({ testname: this.fb.control(this.lds.loginName$.getValue(), [Validators.required, Validators.minLength(3)]), testpw: this.fb.control('', [Validators.required, Validators.minLength(3)]) @@ -106,14 +103,14 @@ export class StartComponent implements OnInit { loginTokenUntyped => { if (loginTokenUntyped instanceof ServerError) { const e = loginTokenUntyped as ServerError; - this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.lds.globalErrorMsg$.next(e); // no change in other data } else { this.validCodes = []; this.bookletlist = []; this.lds.personToken$.next(''); this.lds.personCode$.next(''); - this.lds.globalErrorMsg$.next(''); + this.lds.globalErrorMsg$.next(null); this.lds.workspaceName$.next(''); this.lds.bookletsByCode$.next(null); this.lds.bookletData$.next([]); @@ -129,10 +126,11 @@ export class StartComponent implements OnInit { loginDataUntyped => { if (loginDataUntyped instanceof ServerError) { const e = loginDataUntyped as ServerError; - this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.lds.globalErrorMsg$.next(e); this.lds.loginToken$.next(''); } else { const loginData = loginDataUntyped as LoginData; + this.lds.globalErrorMsg$.next(null); this.lds.personToken$.next(''); this.lds.bookletsByCode$.next(loginData.booklets); this.lds.bookletData$.next([]); @@ -226,12 +224,14 @@ export class StartComponent implements OnInit { bookletIdUntyped => { if (bookletIdUntyped instanceof ServerError) { const e = bookletIdUntyped as ServerError; - this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.lds.globalErrorMsg$.next(e); } else { const bookletId = bookletIdUntyped as number; + this.lds.globalErrorMsg$.next(null); if (bookletId > 0) { this.lds.bookletDbId$.next(bookletId); this.lds.bookletLabel$.next(b.label); + this.lds.globalErrorMsg$.next(null); // ************************************************ // by setting bookletDbId$ the test-controller will load the booklet @@ -239,7 +239,7 @@ export class StartComponent implements OnInit { // ************************************************ } else { - this.lds.globalErrorMsg$.next('ungültige Anmeldung'); + this.lds.globalErrorMsg$.next(new ServerError(401, 'ungültige Anmeldung', 'start.component')); } } } @@ -249,12 +249,13 @@ export class StartComponent implements OnInit { startDataUntyped => { if (startDataUntyped instanceof ServerError) { const e = startDataUntyped as ServerError; - this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.lds.globalErrorMsg$.next(e); } else { const startData = startDataUntyped as PersonTokenAndBookletId; if (startData.b > 0) { this.lds.personToken$.next(startData.pt); + this.lds.globalErrorMsg$.next(null); this.lds.bookletLabel$.next(b.label); this.lds.bookletDbId$.next(startData.b); // as last to trigger auth with success! // ************************************************ @@ -264,14 +265,14 @@ export class StartComponent implements OnInit { // ************************************************ } else { - this.lds.globalErrorMsg$.next('ungültige Anmeldung'); + this.lds.globalErrorMsg$.next(new ServerError(401, 'ungültige Anmeldung', 'start.component')); } } } ); - } + } } else { - this.lds.globalErrorMsg$.next('ungültige Anmeldung'); + this.lds.globalErrorMsg$.next(new ServerError(401, 'ungültige Anmeldung', 'start.component')); } } @@ -298,9 +299,10 @@ export class StartComponent implements OnInit { finOkUntyped => { if (finOkUntyped instanceof ServerError) { const e = finOkUntyped as ServerError; - this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.lds.globalErrorMsg$.next(e); } else { const finOK = finOkUntyped as boolean; + this.lds.globalErrorMsg$.next(null); if (finOK) { this.showLoginForm = false; this.showCodeForm = false; @@ -354,7 +356,7 @@ export class StartButtonData { bs.getBookletStatusByNameAndLoginToken(loginToken, code, this.id, this.label).subscribe(respDataUntyped => { if (respDataUntyped instanceof ServerError) { const e = respDataUntyped as ServerError; - this.statustxt = e.code.toString() + ': ' + e.label; + this.statustxt = e.code.toString() + ': ' + e.labelNice; } else { const respData = respDataUntyped as BookletStatus; this.statustxt = respData.statusLabel; @@ -369,7 +371,7 @@ export class StartButtonData { bs.getBookletStatusByNameAndPersonToken(personToken, this.id).subscribe(respDataUntyped => { if (respDataUntyped instanceof ServerError) { const e = respDataUntyped as ServerError; - this.statustxt = e.code.toString() + ': ' + e.label; + this.statustxt = e.code.toString() + ': ' + e.labelNice; } else { const respData = respDataUntyped as BookletStatus; this.statustxt = respData.statusLabel; diff --git a/src/app/test-controller/backend.service.ts b/src/app/test-controller/backend.service.ts index 0bf91faf..02373ae3 100644 --- a/src/app/test-controller/backend.service.ts +++ b/src/app/test-controller/backend.service.ts @@ -1,3 +1,4 @@ +import { ServerError, ErrorHandler } from './../backend.service'; import { Injectable, Inject } from '@angular/core'; import { HttpClient, HttpParams, HttpHeaders, HttpEvent, HttpErrorResponse } from '@angular/common/http'; import { ResponseContentType } from '@angular/http'; @@ -11,36 +12,12 @@ import { Authorisation } from '../logindata.service'; }) export class BackendService { - private lastBookletState = ''; - private lastUnitResponses = ''; - private restorePoints: {[unitname: string]: string} = {}; - private itemplayers: {[filename: string]: string} = {}; - constructor( @Inject('SERVER_URL') private serverUrl: string, private http: HttpClient) { this.serverUrl = this.serverUrl + 'php_tc/'; } - private normaliseFileName(fn: string, ext: string): string { - fn = fn.toUpperCase(); - ext = ext.toUpperCase(); - if (ext.slice(0, 1) !== '.') { - ext = '.' + ext; - } - - if (fn.slice(-(ext.length)) === ext) { - return fn; - } else { - return fn + ext; - } - } - - // 7777777777777777777777777777777777777777777777777777777777777777777777 - getUnitRestorePoint(unitname: string): string { - return this.restorePoints[unitname]; - } - // 7777777777777777777777777777777777777777777777777777777777777777777777 saveUnitReview(auth: Authorisation, unit: string, priority: number, categories: string, entry: string): Observable<boolean | ServerError> { @@ -53,7 +30,7 @@ export class BackendService { .post<boolean>(this.serverUrl + 'addUnitReview.php', {au: auth.toAuthString(), u: unit, p: priority, c: categories, e: entry}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -68,7 +45,7 @@ export class BackendService { .post<boolean>(this.serverUrl + 'addBookletReview.php', {au: auth.toAuthString(), p: priority, c: categories, e: entry}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -82,7 +59,7 @@ export class BackendService { return this.http .post<BookletData>(this.serverUrl + 'getBookletData.php', {au: auth.toAuthString()}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -97,70 +74,27 @@ export class BackendService { return this.http .post<UnitData>(this.serverUrl + 'getUnitData.php', {au: auth.toAuthString(), u: unitid}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } - // 7777777777777777777777777777777777777777777777777777777777777777777777 - loadItemplayerOk(auth: Authorisation, unitDefinitionType: string): Observable<boolean> { - unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html'); - if (this.itemplayers.hasOwnProperty(unitDefinitionType)) { - return of(true); - } else { - // to avoid multiple calls before returning: - this.itemplayers[unitDefinitionType] = null; - return this.getUnitResourceTxt(auth, unitDefinitionType) - .pipe( - switchMap(myData => { - if (myData instanceof ServerError) { - return of(false); - } else { - const itemplayerData = myData as string; - if (itemplayerData.length > 0) { - this.itemplayers[unitDefinitionType] = itemplayerData; - return of(true); - } else { - return of(false); - } - } - })); - } - } - - - // 7777777777777777777777777777777777777777777777777777777777777777777777 - getItemplayer(unitDefinitionType: string): string { - unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html'); - if ((unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType)) { - return this.itemplayers[unitDefinitionType]; - } else { - return ''; - } - } - - // 7777777777777777777777777777777777777777777777777777777777777777777777 - isItemplayerReady(unitDefinitionType: string): boolean { - unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html'); - return (unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType); - } - // 888888888888888888888888888888888888888888888888888888888888888888 - setBookletStatus(sessiontoken: string, state: {}): Observable<string | ServerError> { + setBookletStatus(auth: Authorisation, state: {}): Observable<string | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; - if ((sessiontoken + JSON.stringify(state)) === this.lastBookletState) { - return new Observable(null); - } else { - this.lastBookletState = sessiontoken + JSON.stringify(state); + // if ((sessiontoken + JSON.stringify(state)) === this.lastBookletState) { + // return new Observable(null); + // } else { + // this.lastBookletState = sessiontoken + JSON.stringify(state); return this.http - .post<string>(this.serverUrl + 'setBookletStatus.php', {st: sessiontoken, state: state}, httpOptions) + .post<string>(this.serverUrl + 'setBookletStatus.php', {au: auth.toAuthString(), state: state}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); - } + // } } // 888888888888888888888888888888888888888888888888888888888888888888 @@ -183,7 +117,7 @@ export class BackendService { } return window.btoa(str64); }), - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -199,7 +133,7 @@ export class BackendService { return this.http .post<string>(this.serverUrl + 'getUnitResource64.php', {st: sessiontoken, r: resId}, myHttpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -215,7 +149,7 @@ export class BackendService { return this.http .post<string>(this.serverUrl + 'getUnitResourceTxt.php', {au: auth.toAuthString(), r: resId}, myHttpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -230,7 +164,7 @@ export class BackendService { return this.http .post<boolean>(this.serverUrl + 'setUnitResponse.php', {au: auth.toAuthString(), u: unit, d: JSON.stringify(unitdata)}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -241,11 +175,10 @@ export class BackendService { 'Content-Type': 'application/json' }) }; - this.restorePoints[unit] = unitdata; return this.http .post<boolean>(this.serverUrl + 'setUnitRestorePoint.php', {au: auth.toAuthString(), u: unit, d: JSON.stringify(unitdata)}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } @@ -259,46 +192,13 @@ export class BackendService { return this.http .post<boolean>(this.serverUrl + 'setUnitLog.php', {au: auth.toAuthString(), u: unit, d: unitdata}, httpOptions) .pipe( - catchError(this.handleError) + catchError(ErrorHandler.handle) ); } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - private handleError(errorObj: HttpErrorResponse): Observable<ServerError> { - const myreturn = new ServerError(errorObj.status, 'Fehler bei Datenübertragung'); - - if (errorObj.status === 401) { - myreturn.label = 'Fehler: Zugriff verweigert - bitte (neu) anmelden!'; - } else if (errorObj.status === 503) { - myreturn.label = 'Fehler: Server meldet Datenbankproblem.'; - } else if (errorObj.error instanceof ErrorEvent) { - myreturn.label = 'Fehler: ' + (<ErrorEvent>errorObj.error).message; - } else { - myreturn.label = 'Fehler: ' + errorObj.message; - } - - return of(myreturn); - } - - private handleErrorSimple(errorObj: HttpErrorResponse): Observable<boolean> { - const myreturn = new ServerError(errorObj.status, 'Fehler bei Datenübertragung'); - - return of(false); - } } // ############################################################################################# -// class instead of interface to be able to use instanceof to check type -export class ServerError { - public code: number; - public label: string; - constructor(code: number, label: string) { - this.code = code; - this.label = label; - } -} - export interface BookletData { xml: string; locked: boolean; @@ -310,8 +210,3 @@ export interface UnitData { restorepoint: string; status: {}; } - -export interface ServerError { - code: number; - label: string; -} diff --git a/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts b/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts index 7867786a..f5d5f536 100644 --- a/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts +++ b/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts @@ -1,5 +1,6 @@ import { FormGroup } from '@angular/forms'; -import { BackendService, ServerError } from './../backend.service'; +import { BackendService } from './../backend.service'; +import { ServerError } from './../../backend.service'; import { MatDialog, MatSnackBar } from '@angular/material'; import { Component, OnInit } from '@angular/core'; import { TestControllerService } from '../test-controller.service'; @@ -64,7 +65,7 @@ export class TcMenuButtonsComponent implements OnInit { ).subscribe(myData => { if (myData instanceof ServerError) { const e = myData as ServerError; - this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.label, '', {duration: 3000}); + this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.labelNice, '', {duration: 3000}); } else { const ok = myData as boolean; if (ok) { @@ -83,7 +84,7 @@ export class TcMenuButtonsComponent implements OnInit { ).subscribe(myData => { if (myData instanceof ServerError) { const e = myData as ServerError; - this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.label, '', {duration: 3000}); + this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.labelNice, '', {duration: 3000}); } else { const ok = myData as boolean; if (ok) { diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css new file mode 100644 index 00000000..d6d4d8e8 --- /dev/null +++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css @@ -0,0 +1,18 @@ +button { + margin: 0px 10px 0px 10px; +} + +div.active-bar, div.not-active-bar { + position: relative; + top: 24px; + width: 100%; + height: 10px; +} + +div.active-bar { + background-color: yellow; +} + +div.not-active-bar { + background-color: transparent; +} diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html new file mode 100644 index 00000000..5f6e8e5c --- /dev/null +++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html @@ -0,0 +1,8 @@ +<div> + <div class="active-bar" *ngIf="isActive"></div> + <div class="not-active-bar" *ngIf="!isActive"></div> + <button mat-raised-button (click)="sideNaviButtonClick()" + [disabled]="unitData.locked" > + {{ unitData.sequenceId + 1 }} + </button> +</div> diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts new file mode 100644 index 00000000..49cf33c1 --- /dev/null +++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TcSidenaviButtonComponent } from './tc-sidenavi-button.component'; + +describe('TcSidenaviButtonComponent', () => { + let component: TcSidenaviButtonComponent; + let fixture: ComponentFixture<TcSidenaviButtonComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TcSidenaviButtonComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TcSidenaviButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts new file mode 100644 index 00000000..4078858f --- /dev/null +++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts @@ -0,0 +1,33 @@ +import { UnitDef, TestControllerService } from './../test-controller.service'; +import { Component, OnInit, Input } from '@angular/core'; + +@Component({ + selector: 'tc-sidenavi-button', + templateUrl: './tc-sidenavi-button.component.html', + styleUrls: ['./tc-sidenavi-button.component.css'] +}) +export class TcSidenaviButtonComponent implements OnInit { + @Input() unitData: UnitDef = null; + isActive = false; + constructor( + private tcs: TestControllerService + ) { + this.tcs.currentUnitPos$.subscribe(up => { + if (this.unitData !== null) { + this.isActive = up === this.unitData.sequenceId; + } + }); + } + + ngOnInit() { + if (this.unitData !== null) { + this.isActive = this.tcs.currentUnitPos$.getValue() === this.unitData.sequenceId; + } + } + + sideNaviButtonClick() { + if (this.unitData !== null) { + this.tcs.goToUnitByPosition(this.unitData.sequenceId); + } + } +} diff --git a/src/app/test-controller/test-controller.component.html b/src/app/test-controller/test-controller.component.html index 4350cc88..b5dab95e 100644 --- a/src/app/test-controller/test-controller.component.html +++ b/src/app/test-controller/test-controller.component.html @@ -5,10 +5,7 @@ <div class="page-body" fxLayout="row" iqbResizeIFrameChild> <div class="tcSidenav" name="sideNav" fxFlex="110px" fxLayout="column" fxLayoutAlign="start center" fxLayoutGap="5px"> <p>Aufgaben</p> - <button mat-raised-button (click)="sideNaviButtonClick(u.sequenceId)" - [disabled]="u.locked" *ngFor="let u of allUnits"> - {{ u.sequenceId + 1 }} - </button> + <tc-sidenavi-button *ngFor="let u of allUnits" [unitData]="u"></tc-sidenavi-button> </div> <div fxFlex> <router-outlet></router-outlet> diff --git a/src/app/test-controller/test-controller.component.ts b/src/app/test-controller/test-controller.component.ts index 02470afa..bf38963f 100644 --- a/src/app/test-controller/test-controller.component.ts +++ b/src/app/test-controller/test-controller.component.ts @@ -1,4 +1,6 @@ -import { BackendService, ServerError, BookletData } from './backend.service'; +import { ServerError } from './../backend.service'; +import { BackendService, BookletData } from './backend.service'; + import { LogindataService } from './../logindata.service'; import { TestControllerService, UnitDef, BookletDef } from './test-controller.service'; import { Component, OnInit } from '@angular/core'; @@ -10,9 +12,9 @@ import { Component, OnInit } from '@angular/core'; export class TestControllerComponent implements OnInit { private showUnitComponent = false; private allUnits: UnitDef[] = []; - private errorMsg = ''; private statusMsg = ''; private dataLoading = false; + private myLastAuthString = ''; // to avoid double load constructor ( private tcs: TestControllerService, @@ -27,44 +29,49 @@ export class TestControllerComponent implements OnInit { } this.updateStatus(); }); - this.lds.globalErrorMsg$.subscribe(m => this.errorMsg = m); this.tcs.currentUnitPos$.subscribe(u => this.updateStatus()); } ngOnInit() { - this.loadBooklet(); + this.loadBooklet('init'); this.lds.authorisation$.subscribe(authori => { - this.loadBooklet(); + this.loadBooklet('subsc'); }); } - private loadBooklet() { + private loadBooklet(s: string) { const auth = this.lds.authorisation$.getValue(); if (auth == null) { - console.log('load booklet (null)'); this.resetBookletData(); + this.myLastAuthString = ''; } else { - console.log('load booklet'); - this.dataLoading = true; - this.bs.getBookletData(auth).subscribe(myData => { - if (myData instanceof ServerError) { - const e = myData as ServerError; - this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); - this.tcs.booklet$.next(null); - this.tcs.currentUnitPos$.next(-1); - } else { - this.lds.globalErrorMsg$.next(''); - const myBookletData = myData as BookletData; - const myBookletDef = new BookletDef(myBookletData); - myBookletDef.loadUnits(this.bs, auth).subscribe(okList => { - this.dataLoading = false; - this.tcs.booklet$.next(myBookletDef); - this.tcs.showNaviButtons$.next(myBookletDef.unlockedUnitCount() > 1); + if (this.myLastAuthString === auth.toAuthString()) { + console.log('not doubling'); + } else { + this.myLastAuthString = auth.toAuthString(); + + console.log('load booklet ' + auth.toAuthString() + ' ' + s); + this.dataLoading = true; + this.bs.getBookletData(auth).subscribe(myData => { + if (myData instanceof ServerError) { + const e = myData as ServerError; + this.lds.globalErrorMsg$.next(e); + this.tcs.booklet$.next(null); this.tcs.currentUnitPos$.next(-1); - this.tcs.goToUnitByPosition(myBookletData.u); - }); - } - }); + } else { + this.lds.globalErrorMsg$.next(null); + const myBookletData = myData as BookletData; + const myBookletDef = new BookletDef(myBookletData); + myBookletDef.loadUnits(this.bs, this.tcs, auth).subscribe(okList => { + this.dataLoading = false; + this.tcs.booklet$.next(myBookletDef); + this.tcs.showNaviButtons$.next(myBookletDef.unlockedUnitCount() > 1); + this.tcs.currentUnitPos$.next(-1); + this.tcs.goToUnitByPosition(myBookletData.u); + }); + } + }); + } } } @@ -86,7 +93,7 @@ export class TestControllerComponent implements OnInit { if (cu >= 0) { this.statusMsg = ''; } else { - this.tcs.pageTitle$.next('IQB-Testcenter'); + // this.tcs.pageTitle$.next('IQB-Testcenter'); if (this.allUnits.length === 0) { this.statusMsg = 'Es stehen keine Informationen über ein gewähltes Testheft zur Verfügung.'; @@ -107,8 +114,4 @@ export class TestControllerComponent implements OnInit { } this.showUnitComponent = this.statusMsg.length === 0; } - - sideNaviButtonClick(targetId: number) { - this.tcs.goToUnitByPosition(targetId); - } } diff --git a/src/app/test-controller/test-controller.module.ts b/src/app/test-controller/test-controller.module.ts index fd1b2604..e71c7cc9 100644 --- a/src/app/test-controller/test-controller.module.ts +++ b/src/app/test-controller/test-controller.module.ts @@ -15,6 +15,7 @@ import { TcNaviButtonsComponent } from './tc-navi-buttons/tc-navi-buttons.compon import { FlexLayoutModule } from '@angular/flex-layout'; import { ReviewDialogComponent } from './tc-menu-buttons/review-dialog.component'; import { ReactiveFormsModule } from '../../../node_modules/@angular/forms'; +import { TcSidenaviButtonComponent } from './tc-sidenavi-button/tc-sidenavi-button.component'; @NgModule({ @@ -41,7 +42,8 @@ import { ReactiveFormsModule } from '../../../node_modules/@angular/forms'; ResizeIFrameChildDirective, TcMenuButtonsComponent, TcNaviButtonsComponent, - ReviewDialogComponent + ReviewDialogComponent, + TcSidenaviButtonComponent ], entryComponents: [ ReviewDialogComponent diff --git a/src/app/test-controller/test-controller.service.ts b/src/app/test-controller/test-controller.service.ts index 07a39b37..7e522244 100644 --- a/src/app/test-controller/test-controller.service.ts +++ b/src/app/test-controller/test-controller.service.ts @@ -3,7 +3,8 @@ import { LogindataService, Authorisation } from './../logindata.service'; import { BehaviorSubject, of, Observable, forkJoin, merge } from 'rxjs'; import { Injectable } from '@angular/core'; import { debounceTime, bufferTime, switchMap, map } from 'rxjs/operators'; -import { BackendService, BookletData, ServerError, UnitData } from './backend.service'; +import { BackendService, BookletData, UnitData } from './backend.service'; +import { ServerError} from './../backend.service'; @Injectable({ providedIn: 'root' @@ -27,6 +28,10 @@ export class TestControllerService { public itemplayerPageRequest$ = new BehaviorSubject<string>(''); // )))))))))))))))))))))))))))))))))))))))))))))))) + // buffering itemplayers + + private itemplayers: {[filename: string]: string} = {}; + // public unitname$ = new BehaviorSubject<string>('-'); @@ -84,6 +89,7 @@ export class TestControllerService { }); } + // 66666666666666666666666666666666666666666666666666666666666666666666666666 getUnitForPlayer(unitId): UnitDef { const myBooklet = this.booklet$.getValue(); if (myBooklet === null) { @@ -98,12 +104,65 @@ export class TestControllerService { return null; } + // 7777777777777777777777777777777777777777777777777777777777777777777777 + loadItemplayerOk(auth: Authorisation, unitDefinitionType: string): Observable<boolean> { + unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html'); + if (this.itemplayers.hasOwnProperty(unitDefinitionType)) { + return of(true); + } else { + // to avoid multiple calls before returning: + this.itemplayers[unitDefinitionType] = null; + return this.bs.getUnitResourceTxt(auth, unitDefinitionType) + .pipe( + switchMap(myData => { + if (myData instanceof ServerError) { + return of(false); + } else { + const itemplayerData = myData as string; + if (itemplayerData.length > 0) { + this.itemplayers[unitDefinitionType] = itemplayerData; + return of(true); + } else { + return of(false); + } + } + })); + } + } + + // uppercase and add extension if not part + private normaliseFileName(fn: string, ext: string): string { + fn = fn.toUpperCase(); + ext = ext.toUpperCase(); + if (ext.slice(0, 1) !== '.') { + ext = '.' + ext; + } + + if (fn.slice(-(ext.length)) === ext) { + return fn; + } else { + return fn + ext; + } + } + + // 7777777777777777777777777777777777777777777777777777777777777777777777 + getItemplayer(unitDefinitionType: string): string { + unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html'); + if ((unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType)) { + return this.itemplayers[unitDefinitionType]; + } else { + return ''; + } + } + + goToUnitByPosition(pos: number) { const myBooklet = this.booklet$.getValue(); if (myBooklet !== null) { const unitCount = myBooklet.units.length; if ((pos >= 0 ) && (pos < unitCount)) { this.setCurrentUnit(pos); + this.bs.setBookletStatus(this.lds.authorisation$.getValue(), {u: pos}).subscribe(); this.router.navigateByUrl('/t/u/' + pos.toString()); } } @@ -168,10 +227,10 @@ export class BookletDef { } } - loadUnits(bs: BackendService, auth: Authorisation): Observable<boolean[]> { + loadUnits(bs: BackendService, tcs: TestControllerService, auth: Authorisation): Observable<boolean[]> { const myUnitLoadings = []; for (let i = 0; i < this.units.length; i++) { - myUnitLoadings.push(this.units[i].loadOk(bs, auth)); + myUnitLoadings.push(this.units[i].loadOk(bs, tcs, auth)); } return forkJoin(myUnitLoadings); } @@ -259,13 +318,13 @@ export class UnitDef { return myResources; } - loadOk(bs: BackendService, auth: Authorisation): Observable<boolean> { + loadOk(bs: BackendService, tcs: TestControllerService, auth: Authorisation): Observable<boolean> { return bs.getUnitData(auth, this.id) .pipe( switchMap(myData => { if (myData instanceof ServerError) { const e = myData as ServerError; - this.label = e.code.toString() + ': ' + e.label; + this.label = e.code.toString() + ': ' + e.labelNice; return of(false); } else { const myUnitData = myData as UnitData; @@ -283,8 +342,8 @@ export class UnitDef { this.unitDefinition = defElement.textContent; this.unitDefinitionType = defElement.getAttribute('type'); - return bs.loadItemplayerOk(auth, this.unitDefinitionType).pipe( - map(ok => this.locked = !ok)); + return tcs.loadItemplayerOk(auth, this.unitDefinitionType).pipe( + switchMap(ok => of(this.locked = !ok))); // ________________________ // const resourcesElements = oDOM.documentElement.getElementsByTagName('Resources'); // if (resourcesElements.length > 0) { diff --git a/src/app/test-controller/testdata.service.spec.ts b/src/app/test-controller/testdata.service.spec.ts deleted file mode 100644 index 64bb3050..00000000 --- a/src/app/test-controller/testdata.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { TestdataService } from './testdata.service'; - -describe('TestdataService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [TestdataService] - }); - }); - - it('should be created', inject([TestdataService], (service: TestdataService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/src/app/test-controller/testdata.service.ts b/src/app/test-controller/testdata.service.ts deleted file mode 100644 index 7497fc52..00000000 --- a/src/app/test-controller/testdata.service.ts +++ /dev/null @@ -1,463 +0,0 @@ -import { BehaviorSubject , Observable, Subject } from 'rxjs'; -import { BackendService, BookletData, UnitData, ServerError } from './backend.service'; -import { Injectable, Component, Input, Output, EventEmitter, Pipe } from '@angular/core'; -import { Element } from '@angular/compiler'; -import { mergeAll, switchMap, map } from 'rxjs/operators'; -import { Router } from '@angular/router'; - -@Injectable() -export class TestdataService { - public isSession$ = new BehaviorSubject<boolean>(false); - public statusmessage$ = new BehaviorSubject<string>('Bitte warten!'); - public bookletname$ = new BehaviorSubject<string>('-'); - public unitname$ = new BehaviorSubject<string>('-'); - public pendingItemDefinition$ = new BehaviorSubject<string>(''); - public pendingItemRestorePoint$ = new BehaviorSubject<string>(''); - public pendingItemResources$ = new BehaviorSubject<{[resourceID: string]: string}>(null); - - public postMessage$ = new Subject<MessageEvent>(); - - public restorePoint$ = new BehaviorSubject<string>(''); - public response$ = new BehaviorSubject<string>(''); - public log$ = new BehaviorSubject<string>(''); - private itemplayers = {}; - private unitRestorePoints = {}; - - - - // ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc - constructor( - private bs: BackendService, - private router: Router - ) { -/* this._sessionToken = localStorage.getItem('st'); - if (this._sessionToken === null) { - this._sessionToken = ''; - } - if (this._sessionToken === '') { - this.isSession$.next(false); - } else { - this.isSession$.next(true); - } - - this.currentUnitId$.subscribe(myUnitId => { - this.navPrevEnabled$.next(myUnitId > 0); - this.navNextEnabled$.next((myUnitId >= 0) && (myUnitId < this.allUnits.length - 1)); - }); - - this.postMessage$.subscribe(postData => { - const msgData = postData.data; - const msgType = msgData['type']; - if ((msgType !== undefined) || (msgType !== null)) { - if (msgType.substr(0, 7) === 'OpenCBA') { - // ......................................... - const targetWindow = postData.source; - if (msgType == 'OpenCBA.newData') { - const responseData = playerData['newResponses']; - if ((responseData !== undefined) || (responseData !== null)) { - this.response$.next(responseData); - // this.bs.setUnitResponses(this.sessionToken, this.unitname$.getValue(), responseData) - // .subscribe(); - } - - const restoreData = playerData['newRestorePoint']; - if ((restoreData !== undefined) || (restoreData !== null)) { - this.restorePoint$.next(restoreData); - // this.bs.setUnitRestorePoint(this.sessionToken, this.unitname$.getValue(), restoreData) - // .subscribe(); - } - - const logData = playerData['newLogEntry']; - if ((logData !== undefined) || (logData !== null)) { - this.log$.next(logData); - // this.bs.setUnitLog(this.sessionToken, this.unitname$.getValue(), logData) - // .subscribe(); - } - } - - })*/ -/* this.isSession$.subscribe(isSession => { - if (isSession) { - - } else { - this.updateBookletData('?', [], '', ''); - } - }); */ - } -/* - // ----------------------------------------------------------------- - public addItemPlayer(playerid: string, player: string) { - this.itemplayers[playerid] = player; - } - - // ----------------------------------------------------------------- - public getItemPlayer(playerid: string) { - if (this.itemplayers.hasOwnProperty(playerid)) { - return this.itemplayers[playerid]; - } else { - return ''; - } - } - - // ----------------------------------------------------------------- - public addUnitRestorePoint(unitId: string, restPoint: string) { - this.unitRestorePoints[unitId] = restPoint; - } - - // ----------------------------------------------------------------- - public getUnitRestorePoint(unitId: string) { - if (this.unitRestorePoints.hasOwnProperty(unitId)) { - return this.unitRestorePoints[unitId]; - } else { - return ''; - } - } - - getUnitAt (unitId: any): Observable<UnitDef | ServerError> { - const unitIdNumber = Number(unitId); - if (Number.isNaN(unitId) || (unitId < 0)) { - return new Observable(observer => { - observer.next(null); - observer.complete(); - }); - } else { - if (this.allUnits.length === 0) { - - // first call at the beginning of test -> get booklet - return this.bs.getSessionData(this.sessionToken) - .pipe( - map((bdata: SessionData) => { - - let myBookletName = ''; - - // Create Unit-List - const oParser = new DOMParser(); - const oDOM = oParser.parseFromString(bdata.xml, 'text/xml'); - if (oDOM.documentElement.nodeName === 'Booklet') { - // ________________________ - const metadataElements = oDOM.documentElement.getElementsByTagName('Metadata'); - if (metadataElements.length > 0) { - const metadataElement = metadataElements[0]; - const NameElement = metadataElement.getElementsByTagName('Name')[0]; - myBookletName = NameElement.textContent; - } - - // ________________________ - const unitsElements = oDOM.documentElement.getElementsByTagName('Units'); - if (unitsElements.length > 0) { - const unitsElement = unitsElements[0]; - const unitList = unitsElement.getElementsByTagName('Unit'); - for (let i = 0; i < unitList.length; i++) { - this.allUnits[i] = new UnitDef(unitList[i].getAttribute('name'), unitList[i].getAttribute('title')); - this.allUnits[i].sequenceId = i; - } - } - } - return this.allUnits[unitId]; - })); - } else { - // this.updateBookletData(myBookletName, myUnits, 'Bitte warten', bdata.status); - // }, (err: ServerError) => { - // this.updateBookletData('?', [], err.label, ''); - return new Observable(observer => { - observer.next(this.allUnits[unitId]); - observer.complete(); - }); - } - } - } - - // switchMap because current requests have to cancelled if new fetchUnitData-call arrives - fetchUnitData (myUnit: UnitDef): Observable<UnitDef> { - if (myUnit === null) { - return new Observable(observer => { - observer.next(null); - observer.complete(); - }); - } else { - - return this.bs.getUnit(this.sessionToken, myUnit.name) - .pipe( - switchMap((udata: UnitData) => { - myUnit.restorePoint = udata.restorepoint; - - const oParser = new DOMParser(); - const oDOM = oParser.parseFromString(udata.xml, 'text/xml'); - if (oDOM.documentElement.nodeName === 'Unit') { - // ________________________ - const dataElements = oDOM.documentElement.getElementsByTagName('Data'); - if (dataElements.length > 0) { - const dataElement = dataElements[0]; - myUnit.dataForItemplayer = dataElement.textContent; - } - - // ________________________ - const resourcesElements = oDOM.documentElement.getElementsByTagName('Resources'); - if (resourcesElements.length > 0) { - let ResourceFetchPromises: Promise<number>[]; - ResourceFetchPromises = []; - - // resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - const resourcesElement = resourcesElements[0]; - const rList = resourcesElement.getElementsByTagName('Resource'); - for (let i = 0; i < rList.length; i++) { - const myResource = new ResourceData(rList[i].textContent, rList[i].getAttribute('id')); - myResource.type = rList[i].getAttribute('type'); - myUnit.resources.push(myResource); - - // prepare promise for each resource loading - if (myResource.type === 'itemplayer_html') { - ResourceFetchPromises.push(new Promise((resolve, reject) => { - this.bs.getUnitResourceTxt(this.sessionToken, myResource.filename).subscribe( - (fileAsTxt: string) => { - myResource.dataString = fileAsTxt; - resolve(myResource.dataString.length); - } - ); - })); - } else { - ResourceFetchPromises.push(new Promise((resolve, reject) => { - this.bs.getUnitResource64(this.sessionToken, myResource.filename).subscribe( - (fileAsBase64: string) => { - myResource.dataString = fileAsBase64; - resolve(myResource.dataString.length); - } - ); - })); - } - } - - // run all promises (i. e. resource loading requests) - return Promise.all(ResourceFetchPromises) - .then(promisesReturnValues => { - this.bs.setBookletStatus(this.sessionToken, {u: myUnit.sequenceId}) - .subscribe(); - return myUnit; - }); - - - } else { - return this.bs.setBookletStatus(this.sessionToken, {u: myUnit.sequenceId}) - .pipe( - map(d => myUnit) - ); - } - } else { - return null; - } - })); - } - } - - // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - getUnitId(target: string): number { - let myUnitId = this.currentUnitId$.getValue(); - - if (this.allUnits.length > 0) { - switch (target) { - case 'next': - if (myUnitId < this.allUnits.length - 1) { - myUnitId = myUnitId + 1; - } - break; - - case 'prev': - if (myUnitId > 0) { - myUnitId = myUnitId - 1; - } - break; - - case 'first': - myUnitId = 0; - break; - - case 'last': - myUnitId = this.allUnits.length - 1; - break; - - default: - myUnitId = -1; - break; - } - } else { - myUnitId = 0; - } - return myUnitId; - } - - // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - public gotoPrevUnit() { - this.gotoUnit(this.getUnitId('prev')); - } - - // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - public gotoNextUnit() { - this.gotoUnit(this.getUnitId('next')); - } - - // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - public gotoFirstUnit(lastUnit?: number) { - if (lastUnit == null) { - this.gotoUnit(this.getUnitId('first')); - } else { - this.gotoUnit(lastUnit); - } - } - - // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - private gotoUnit(newUnitId) { - this.router.navigateByUrl('/t/u/' + newUnitId, { skipLocationChange: false }); - } - - // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ - processMessagePost(postData: MessageEvent) { - const msgData = postData.data; - const msgType = msgData['type']; - if ((msgType !== undefined) || (msgType !== null)) { - if (msgType.substr(0, 7) === 'OpenCBA') { - this.postMessage$.next(postData); - } - } - } - - // app.component.ngOnInit sets a listener on 'message'-event. - processMessagePostX(postData: MessageEvent) { - const msgData = postData.data; - const msgType = msgData['type']; - if ((msgType !== undefined) || (msgType !== null)) { - if (msgType.substr(0, 7) === 'OpenCBA') { - // ......................................... - const targetWindow = postData.source; - switch (msgType) { - - // // // // // // // - case 'OpenCBA.stateChanged': - if (msgData['newState'] === 'readyToInitialize') { - let hasData = false; - const initParams = {}; - - const pendingSpec = this.pendingItemDefinition$.getValue(); - if ((pendingSpec !== null) || (pendingSpec.length > 0)) { - initParams['itemSpecification'] = pendingSpec; - hasData = true; - this.pendingItemDefinition$.next(null); - } - const pendingRes = this.pendingItemResources$.getValue(); - if ((pendingRes !== null) || (pendingRes !== {})) { - initParams['itemResources'] = pendingRes; - hasData = true; - this.pendingItemResources$.next({}); - } - const pendingRP = this.pendingItemRestorePoint$.getValue(); - if ((pendingRP !== null) || (pendingRP.length > 0)) { - initParams['restorePoint'] = pendingRP; - hasData = true; - this.pendingItemRestorePoint$.next(null); - } - - if (hasData) { - targetWindow.postMessage({ - type: 'OpenCBA.initItemPlayer', - initParameters: initParams - }, '*'); - } - } - break; - - // // // // // // // - case 'OpenCBA.newData': - const responseData = msgData['newResponses']; - if ((responseData !== undefined) || (responseData !== null)) { - this.bs.setUnitResponses(this.sessionToken, this.unitname$.getValue(), responseData) - .subscribe(); - } - - const restoreData = msgData['newRestorePoint']; - if ((restoreData !== undefined) || (restoreData !== null)) { - this.bs.setUnitRestorePoint(this.sessionToken, this.unitname$.getValue(), restoreData) - .subscribe(); - } - - const logData = msgData['newLogEntry']; - if ((restoreData !== undefined) || (restoreData !== null)) { - this.bs.setUnitLog(this.sessionToken, this.unitname$.getValue(), logData) - .subscribe(); - } - - break; - - // // // // // // // - default: - console.log('processMessagePost unknown message type: ' + msgType); - break; - } - - - // ......................................... - } - } - } - // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ - - - updateSessionToken(newToken: string) { - this._sessionToken = newToken; - if ((newToken !== null) && (newToken.length > 0)) { - localStorage.setItem('st', newToken); - this.isSession$.next(true); - } else { - localStorage.removeItem('st'); - this.isSession$.next(false); - } - } - - updateBookletData(bookletname: string, units: UnitDef[], message: string, status: {}) { - this.allUnits = units; - this.bookletname$.next(bookletname); - this.statusmessage$.next(message); - - if ((status === null) || (status['u'] === undefined)) { - this.gotoFirstUnit(); - } else { - this.gotoUnit(status['u']); - } - } - - updatePageTitle(newTitle: string) { - this.pageTitle$.next(newTitle); - } - - updateUnitId(newUnitId: number) { - if ((newUnitId >= 0) && (newUnitId < this.allUnits.length)) { - this.currentUnitId$.next(newUnitId); - } else { - this.currentUnitId$.next(-1); - } - }*/ -} - - - -// ..................................................................... -export class Testlet { - private _testlets: Testlet[]; - private _units: string; - constructor() { - - } - -} - -// ..................................................................... -export interface NavigationPoint { - title: string; - unitId: number; - path: string; -} - -// ..................................................................... -export class ResourceStore { - -} - diff --git a/src/app/test-controller/unithost/unit-routing.ts b/src/app/test-controller/unithost/unit-routing.ts index 2cf373e7..f172dac6 100644 --- a/src/app/test-controller/unithost/unit-routing.ts +++ b/src/app/test-controller/unithost/unit-routing.ts @@ -1,6 +1,6 @@ import { UnitDef, TestControllerService } from './../test-controller.service'; import { switchMap, map } from 'rxjs/operators'; -import { BackendService, ServerError } from './../backend.service'; +import { BackendService } from './../backend.service'; import { UnithostComponent } from './unithost.component'; import { Injectable, Component } from '@angular/core'; import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, Resolve } from '@angular/router'; @@ -28,8 +28,8 @@ export class UnitActivateGuard implements CanActivate { const newUnit = currentBooklet.getUnitAt(targetUnitSequenceId); if (newUnit.locked) { console.log('unit is locked'); - } else if (!this.bs.isItemplayerReady(newUnit.unitDefinitionType)) { - console.log('itemplayer for unit not available'); + // } else if (!this.bs.isItemplayerReady(newUnit.unitDefinitionType)) { + // console.log('itemplayer for unit not available'); } else { this.tcs.setCurrentUnit(targetUnitSequenceId); } @@ -39,6 +39,13 @@ export class UnitActivateGuard implements CanActivate { } } + // // 7777777777777777777777777777777777777777777777777777777777777777777777 + // isItemplayerReady(unitDefinitionType: string): boolean { + // unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html'); + // return (unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType); + // } + + @Injectable() export class UnitDeactivateGuard implements CanDeactivate<UnithostComponent> { constructor( diff --git a/src/app/test-controller/unithost/unithost.component.html b/src/app/test-controller/unithost/unithost.component.html index 1a69f66a..8fdfecb6 100644 --- a/src/app/test-controller/unithost/unithost.component.html +++ b/src/app/test-controller/unithost/unithost.component.html @@ -1,3 +1,3 @@ -<div id="iFrameHost" class="ityyemplayer-body"> +<div id="iFrameHost"> </div> diff --git a/src/app/test-controller/unithost/unithost.component.ts b/src/app/test-controller/unithost/unithost.component.ts index 346cf44b..5c6d9960 100644 --- a/src/app/test-controller/unithost/unithost.component.ts +++ b/src/app/test-controller/unithost/unithost.component.ts @@ -1,7 +1,9 @@ -import { debounceTime, bufferTime } from 'rxjs/operators'; +import { Authorisation } from './../../logindata.service'; +import { debounceTime, bufferTime, switchMap } from 'rxjs/operators'; import { UnitDef, TestControllerService } from './../test-controller.service'; -import { Subscriber, Subscription, BehaviorSubject } from 'rxjs'; -import { BackendService, ServerError } from './../backend.service'; +import { Subscriber, Subscription, BehaviorSubject, Observable, of } from 'rxjs'; +import { BackendService } from './../backend.service'; +import { ServerError } from './../../backend.service'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { OnDestroy } from '@angular/core/src/metadata/lifecycle_hooks'; @@ -36,6 +38,11 @@ export class UnithostComponent implements OnInit, OnDestroy { public response$ = new BehaviorSubject<string>(''); public log$ = new BehaviorSubject<string>(''); + // buffering restorePoints + private lastBookletState = ''; + private lastUnitResponses = ''; + private restorePoints: {[unitname: string]: string} = {}; + constructor( private tcs: TestControllerService, private bs: BackendService, @@ -143,6 +150,7 @@ export class UnithostComponent implements OnInit, OnDestroy { } } + this.restorePoints[this.myUnitName] = data; this.bs.setUnitRestorePoint(this.lds.authorisation$.getValue(), this.myUnitName, data) .subscribe(d => { if (d === false) { @@ -205,7 +213,15 @@ export class UnithostComponent implements OnInit, OnDestroy { this.loadItemplayer(); }); - // this.tcs.currentUnitPos$.subscribe(cu => this.loadItemplayer()); + this.lds.authorisation$.subscribe(auth => { + this.restorePoints = {}; + }); + + this.tcs.currentUnitPos$.subscribe(up => { + if (up >= 0) { + this.loadItemplayer(); + } + }); } loadItemplayer() { @@ -214,6 +230,7 @@ export class UnithostComponent implements OnInit, OnDestroy { } const currentUnitId = this.tcs.currentUnitPos$.getValue(); const booklet = this.tcs.booklet$.getValue(); + console.log('load Itemplayer 1st'); if ((currentUnitId >= 0) && (this.myUnitNumber === currentUnitId) && (booklet !== null)) { console.log('load Itemplayer - currentUnitId: ' + currentUnitId); const currentUnit = booklet.getUnitAt(currentUnitId); @@ -221,14 +238,14 @@ export class UnithostComponent implements OnInit, OnDestroy { this.myUnitName = currentUnit.id; this.iFrameItemplayer = <HTMLIFrameElement>document.createElement('iframe'); - this.iFrameItemplayer.setAttribute('srcdoc', this.bs.getItemplayer(currentUnit.unitDefinitionType)); + this.iFrameItemplayer.setAttribute('srcdoc', this.tcs.getItemplayer(currentUnit.unitDefinitionType)); this.iFrameItemplayer.setAttribute('sandbox', 'allow-forms allow-scripts allow-same-origin'); this.iFrameItemplayer.setAttribute('class', 'unitHost'); const sideNavElement = document.getElementsByName('sideNav')[0]; this.iFrameItemplayer.setAttribute('height', String(sideNavElement.clientHeight - 5)); this.pendingUnitDefinition$.next(currentUnit.unitDefinition); - const restorePoint = this.bs.getUnitRestorePoint(this.myUnitName); + const restorePoint = this.restorePoints[this.myUnitName]; if ((restorePoint === null) || (restorePoint === undefined)) { this.pendingRestorePoint$.next(currentUnit.restorePoint); diff --git a/src/environments/environment.ts b/src/environments/environment.ts index b5d68c88..f3e2e760 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,7 +4,7 @@ export const environment = { production: false, - testcenterUrl: 'https://mdr2.iqb.hu-berlin.de/', + testcenterUrl: 'https://ocba.iqb.hu-berlin.de/', appName: 'IQB-Testcenter', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', appVersion: '0 (dev)' -- GitLab