diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts index cd480ad4c47b2677e719d7b6728d4cccc943968a..c3e67665098348b03d0cb23dacac7105257732fd 100644 --- a/src/app/about/about.component.ts +++ b/src/app/about/about.component.ts @@ -6,8 +6,8 @@ import { Component, Inject } from '@angular/core'; export class AboutComponent { constructor( - @Inject('APP_NAME') private appName: string, - @Inject('APP_PUBLISHER') private appPublisher: string, - @Inject('APP_VERSION') private appVersion: string + @Inject('APP_NAME') public appName: string, + @Inject('APP_PUBLISHER') public appPublisher: string, + @Inject('APP_VERSION') public appVersion: string ) { } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 38945859bf657b6795909d3b74b53c805b98cbe7..3ac1b21b34d3112dff58b62b5ec35e9643a4c8f8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -30,7 +30,7 @@ export class AppComponent implements OnInit { }); this.bs.getSysConfig().subscribe(sc => { - this.mds.setSysConfig(sc); + this.mds.setCostumTextsApp(sc); // restore login status if stored in localStorage const loginToken = localStorage.getItem('lt'); if (loginToken !== null) { @@ -56,6 +56,7 @@ export class AppComponent implements OnInit { this.bs.getLoginData(loginToken, personToken, bookletDbId).subscribe(ld => { if (ld instanceof ServerError) { this.mds.setNewLoginData(); + this.mds.setCostumTextsLogin(); } else { const loginData = ld as LoginData; loginData.logintoken = loginToken; @@ -65,13 +66,16 @@ export class AppComponent implements OnInit { loginData.booklet = 0; } this.mds.setNewLoginData(loginData); + this.mds.setCostumTextsLogin(loginData.costumTexts); } }); } else { this.mds.setNewLoginData(); + this.mds.setCostumTextsLogin(); } } else { this.mds.setNewLoginData(); + this.mds.setCostumTextsLogin(); } }); } diff --git a/src/app/app.interfaces.ts b/src/app/app.interfaces.ts index 956860ff993f9d6310e2a2f5c1a2dbc176280800..5696e4a0be8736fc54e7e2b60f29800856d28903 100644 --- a/src/app/app.interfaces.ts +++ b/src/app/app.interfaces.ts @@ -23,6 +23,7 @@ export interface LoginData { code: string; booklet: number; bookletlabel: string; + costumTexts: KeyValuePair; } export interface BookletStatus { @@ -45,13 +46,3 @@ export interface KeyValuePair { export interface KeyValuePairNumber { [K: string]: number; } - -export enum SysConfigKey { - testEndButtonText = 'testEndButtonText', - bookletSelectPrompt = 'bookletSelectPrompt', - bookletSelectTitle = 'bookletSelectTitle', - bookletSelectPromptOne = 'bookletSelectPromptOne', - bookletSelectPromptMany = 'bookletSelectPromptMany', - codeInputTitle = 'codeInputTitle', - codeInputPrompt = 'codeInputPrompt' -} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c66d1aa36c36b0a32c747bb0ed560f1cd97fb0d8..f5ca540c10b54cdf5c5fc37ca77743a1b311f7a2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,6 +18,7 @@ import { LocationStrategy, HashLocationStrategy } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { ErrormsgComponent } from './errormsg/errormsg.component'; import { httpInterceptorProviders } from './app.interceptor'; +import { CustomTextPipe } from './custom-text.pipe'; @NgModule({ @@ -25,7 +26,8 @@ import { httpInterceptorProviders } from './app.interceptor'; AppComponent, StartComponent, AboutComponent, - ErrormsgComponent + ErrormsgComponent, + CustomTextPipe ], imports: [ BrowserModule, diff --git a/src/app/custom-text.pipe.spec.ts b/src/app/custom-text.pipe.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..72be11de02c2bb2795f98901cadc4b0697911b78 --- /dev/null +++ b/src/app/custom-text.pipe.spec.ts @@ -0,0 +1,8 @@ +import { CustomTextPipe } from './custom-text.pipe'; + +describe('CustomTextPipe', () => { + it('create an instance', () => { + const pipe = new CustomTextPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/custom-text.pipe.ts b/src/app/custom-text.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..4dff10b2301b8338ba78768d255019e077c287af --- /dev/null +++ b/src/app/custom-text.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { MainDataService } from './maindata.service'; + +@Pipe({ + name: 'customText' +}) +export class CustomTextPipe implements PipeTransform { + + constructor ( private mds: MainDataService ) {} + + transform(valueForChangeDetection: any, key: string): string { + return this.mds.getCostumText(key); + } +} diff --git a/src/app/errormsg/errormsg.component.ts b/src/app/errormsg/errormsg.component.ts index 7498ff8d60ea9eda9df10e5f12cace33c6840bc0..94527f5d8c5f299686f2d378a26e3f0b898c5f43 100644 --- a/src/app/errormsg/errormsg.component.ts +++ b/src/app/errormsg/errormsg.component.ts @@ -9,7 +9,7 @@ import { Subscription } from 'rxjs'; styleUrls: ['./errormsg.component.css'] }) export class ErrormsgComponent implements OnInit, OnDestroy { - private errorMsg: ServerError = null; + public errorMsg: ServerError = null; private globalErrorMsgSubscription: Subscription = null; constructor( diff --git a/src/app/iqb-common/message-dialog/message-dialog.component.ts b/src/app/iqb-common/message-dialog/message-dialog.component.ts index 948aaff56c21afcb27c6fbae5f1a56e7ea494176..18377c47c9998a942adc287f79c428a810f317d9 100644 --- a/src/app/iqb-common/message-dialog/message-dialog.component.ts +++ b/src/app/iqb-common/message-dialog/message-dialog.component.ts @@ -1,6 +1,12 @@ import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; import { Component, OnInit, Inject } from '@angular/core'; +export enum MessageType { + error, + warning, + info +} + @Component({ templateUrl: './message-dialog.component.html', styleUrls: ['./message-dialog.component.css'] @@ -32,12 +38,6 @@ export class MessageDialogComponent implements OnInit { } } -export enum MessageType { - error, - warning, - info -} - export interface MessageDialogData { type: MessageType; title: string; diff --git a/src/app/maindata.service.ts b/src/app/maindata.service.ts index 2e533be66d32583be5a5be12a65720830379c9f1..4a2e1f778c1b485211bf3ee1d9617f3284d82931 100644 --- a/src/app/maindata.service.ts +++ b/src/app/maindata.service.ts @@ -2,13 +2,13 @@ import { KeyValuePair } from './test-controller/test-controller.interfaces'; import { ServerError, BackendService } from './backend.service'; import { BehaviorSubject, Subject, forkJoin } from 'rxjs'; import { Injectable } from '@angular/core'; -import { LoginData, SysConfigKey } from './app.interfaces'; +import { LoginData } from './app.interfaces'; @Injectable({ providedIn: 'root' }) export class MainDataService { - private standardLoginData: Readonly<LoginData> = { + private static defaultLoginData: LoginData = { logintoken: '', persontoken: '', mode: '', @@ -18,12 +18,40 @@ export class MainDataService { booklets: null, code: '', booklet: 0, - bookletlabel: '' + bookletlabel: '', + costumTexts: {} + }; + private static defaultCostumTexts = { + 'app_title': 'IQB-Testcenter', + 'app_loginErrorMsg': 'Die Anmeldedaten sind nicht korrekt.', + 'login_testEndButtonText': 'Test beenden', + 'login_bookletSelectPrompt': 'Bitte wählen', + 'login_bookletSelectTitle': 'Bitte wählen', + 'login_bookletSelectPromptOne': 'Bitte klick auf die Schaltfläche links, um den Test zu starten!', + 'login_bookletSelectPromptMany': 'Bitte klick auf eine der Schaltflächen links, um einen Test zu starten!', + 'login_codeInputPrompt': 'Bitte Log-in eingeben, der auf dem Zettel steht!', + 'login_codeInputTitle': 'Log-in eingeben', + 'booklet_msgPresentationNotCompleteTitle': + 'Weiterblättern nicht möglich', + 'booklet_msgPresentationNotCompleteText': + 'Du kannst erst weiterblättern, wenn Audio-Dateien vollständig abgespielt wurden ' + + 'und wenn du in allen Fenstern bis ganz nach unten gescrollt hast.', + 'booklet_codeToEnterTitle': 'Freigabecode', + 'booklet_codeToEnterPrompt': 'Bitte gib den Code ein, der angesagt wurde!', + 'booklet_msgSoonTimeOver5Minutes': 'Du hast noch 5 Minuten Zeit für die Bearbeitung der Aufgaben in diesem Abschnitt.', + 'booklet_msgSoonTimeOver1Minute': 'Du hast noch 1 Minute Zeit für die Bearbeitung der Aufgaben in diesem Abschnitt.', + 'booklet_msgTimerStarted': 'Die Bearbeitungszeit für diesen Abschnitt hat begonnen: ', + 'booklet_msgTimerCancelled': 'Die Bearbeitung des Abschnittes wurde abgebrochen.', + 'booklet_msgTimeOver': 'Die Bearbeitung des Abschnittes ist beendet.', + 'booklet_warningLeaveTimerBlockTitle': 'Aufgabenabschnitt verlassen?', + 'booklet_warningLeaveTimerBlockPrompt': 'Wenn du jetzt weiterblätterst, ist die Bearbeitungszeit beendet und du kannst nicht zurück.', }; - public loginData$ = new BehaviorSubject<LoginData>(this.standardLoginData); + public loginData$ = new BehaviorSubject<LoginData>(MainDataService.defaultLoginData); public globalErrorMsg$ = new BehaviorSubject<ServerError>(null); - private _sysConfig: KeyValuePair = null; + public refreshCostumTexts = false; + private _costumTextsApp: KeyValuePair = {}; + private _costumTextsLogin: KeyValuePair = {}; // set by app.component.ts public postMessage$ = new Subject<MessageEvent>(); @@ -33,16 +61,17 @@ export class MainDataService { // ensures consistency setNewLoginData(logindata?: LoginData) { const myLoginData: LoginData = { - logintoken: this.standardLoginData.logintoken, - persontoken: this.standardLoginData.persontoken, - mode: this.standardLoginData.mode, - groupname: this.standardLoginData.groupname, - loginname: this.standardLoginData.loginname, - workspaceName: this.standardLoginData.workspaceName, - booklets: this.standardLoginData.booklets, - code: this.standardLoginData.code, - booklet: this.standardLoginData.booklet, - bookletlabel: this.standardLoginData.bookletlabel + logintoken: MainDataService.defaultLoginData.logintoken, + persontoken: MainDataService.defaultLoginData.persontoken, + mode: MainDataService.defaultLoginData.mode, + groupname: MainDataService.defaultLoginData.groupname, + loginname: MainDataService.defaultLoginData.loginname, + workspaceName: MainDataService.defaultLoginData.workspaceName, + booklets: MainDataService.defaultLoginData.booklets, + code: MainDataService.defaultLoginData.code, + booklet: MainDataService.defaultLoginData.booklet, + bookletlabel: MainDataService.defaultLoginData.bookletlabel, + costumTexts: MainDataService.defaultLoginData.costumTexts // always ignored except right after getting from backend! }; if (logindata) { @@ -138,25 +167,30 @@ export class MainDataService { } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - setSysConfig(sc: KeyValuePair) { - this._sysConfig = sc; + setCostumTextsApp(sc: KeyValuePair = {}) { + this.refreshCostumTexts = false; + this._costumTextsApp = sc; + this.refreshCostumTexts = true; } - - getSysConfigValue(key: SysConfigKey): string { - if (this._sysConfig !== null) { - if (this._sysConfig.hasOwnProperty(key)) { - return this._sysConfig[key]; + setCostumTextsLogin(sc: KeyValuePair = {}) { + this.refreshCostumTexts = false; + this._costumTextsLogin = sc; + this.refreshCostumTexts = true; + } + getCostumText(key: string): string { + if (this._costumTextsLogin) { + if (this._costumTextsLogin.hasOwnProperty(key)) { + return this._costumTextsLogin[key]; + } + } + if (this._costumTextsApp) { + if (this._costumTextsApp.hasOwnProperty(key)) { + return this._costumTextsApp[key]; } } - switch (key) { - case SysConfigKey.testEndButtonText: return 'Test beenden'; - case SysConfigKey.bookletSelectPrompt: return 'Bitte wählen'; - case SysConfigKey.bookletSelectTitle: return 'Bitte wählen'; - case SysConfigKey.bookletSelectPromptOne: return 'Bitte klick auf die Schaltfläche links, um den Test zu starten!'; - case SysConfigKey.bookletSelectPromptMany: return 'Bitte klick auf eine der Schaltflächen links, um einen Test zu starten!'; - case SysConfigKey.codeInputPrompt: return 'Bitte den Personencode eingeben!'; - case SysConfigKey.codeInputTitle: return 'Personencode eingeben'; + if (MainDataService.defaultCostumTexts.hasOwnProperty(key)) { + return MainDataService.defaultCostumTexts[key]; } - return '?'; + return key; } } diff --git a/src/app/start/start.component.html b/src/app/start/start.component.html index dd49b9d771c4e56915a713e15d14486d9541ef6a..05d7d47632cad2b876ce09d62f249d861ca157c6 100644 --- a/src/app/start/start.component.html +++ b/src/app/start/start.component.html @@ -34,7 +34,7 @@ <!-- - - - - - - - - - - - - - - - - --> <mat-card fxFlex="0 0 400px" *ngIf="showCodeForm"> <form [formGroup]="codeinputform" (ngSubmit)="codeinput()"> - <mat-card-title>{{ codeInputTitle }}</mat-card-title> + <mat-card-title>{{ showCodeForm | customText:'login_codeInputTitle' }}</mat-card-title> <mat-card-content> <mat-form-field> <input matInput formControlName="code" placeholder="Personen-Code"(keyup.enter)="codeinput()"> @@ -49,7 +49,7 @@ <!-- - - - - - - - - - - - - - - - - --> <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="showBookletButtons"> - <mat-card-title>{{ bookletSelectTitle }}</mat-card-title> + <mat-card-title>{{ showBookletButtons | customText:'login_bookletSelectTitle' }}</mat-card-title> <mat-card-content> <div fxLayout="row" fxLayoutGap="10px" fxLayout="column"> <p *ngIf="bookletlist.length === 0"> @@ -73,7 +73,7 @@ <mat-card-content> <div fxLayout="column" fxLayoutGap="20px"> <button mat-raised-button color="primary" [routerLink]="['/t']">Zum Test zurückkehren</button> - <button mat-raised-button color="primary" (click)="stopBooklet()">{{ testEndButtonText }}</button> + <button mat-raised-button color="primary" (click)="stopBooklet()">{{ showTestRunningButtons | customText:'login_testEndButtonText' }}</button> <button mat-raised-button color="foreground" (click)="resetLogin()">Neu anmelden</button> </div> </mat-card-content> @@ -82,7 +82,7 @@ <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> <mat-card fxFlex="0 2 400px" fxLayout="column" class="status"> - <mat-card-title>IQB-Testcenter</mat-card-title> + <mat-card-title>{{ mds.refreshCostumTexts | customText:'app_title' }}</mat-card-title> <!-- - - - - - - - - - - - - - - - - --> <mat-card-content> @@ -110,18 +110,18 @@ <!-- - - - - - - - - - - - - - - - - --> <div *ngIf="showCodeForm"> <p>Für das Starten eines Tests ist die Eingabe eines Personen-Codes nötig.</p> - <p>{{ codeInputPrompt }}</p> + <p>{{ showCodeForm | customText:'login_codeInputPrompt' }}</p> </div> <!-- - - - - - - - - - - - - - - - - --> <div *ngIf="showBookletButtons"> - <p>{{ bookletSelectPrompt }}</p> + <p>{{ showBookletButtons | customText:'login_bookletSelectPrompt' }}</p> </div> <!-- - - - - - - - - - - - - - - - - --> <div *ngIf="showTestRunningButtons"> - <p>Es wird gerade ein Test ausgeführt. Bitte wählen Sie durch Klicken auf eine der beiden Schaltflächen - links, ob der Test fortgesetzt oder beendet werden soll!</p> + <p>Es wird gerade ein Test ausgeführt. Bitte durch Klicken auf eine der beiden Schaltflächen + links wählen, ob der Test fortgesetzt oder beendet werden soll!</p> </div> <div class="error-msg">{{ (mds.globalErrorMsg$ | async)?.labelNice }}</div> diff --git a/src/app/start/start.component.ts b/src/app/start/start.component.ts index e6176aeda64d32e2fb1666f369831036f6659ffa..76d5327f0a7fbf7652d34d302b0e98dcd525e4cd 100644 --- a/src/app/start/start.component.ts +++ b/src/app/start/start.component.ts @@ -1,4 +1,3 @@ -import { SysConfigKey } from './../app.interfaces'; import { MainDataService } from './../maindata.service'; import { Subscription, BehaviorSubject, forkJoin } from 'rxjs'; import { MessageDialogComponent, MessageDialogData, MessageType } from './../iqb-common'; @@ -15,26 +14,24 @@ import { StartButtonData } from './start-button-data.class'; styleUrls: ['./start.component.css'] }) export class StartComponent implements OnInit, OnDestroy { + public dataLoading = false; + public showLoginForm = true; + public showCodeForm = false; + public showBookletButtons = false; + public bookletlist: StartButtonData[] = []; + public showTestRunningButtons = false; + private loginDataSubscription: Subscription = null; - private dataLoading = false; // for template - private showLoginForm = true; - private showCodeForm = false; - private showBookletButtons = false; - private bookletlist: StartButtonData[] = []; - private showTestRunningButtons = false; private validCodes = []; private loginStatusText = ['nicht angemeldet']; private testtakerloginform: FormGroup; private codeinputform: FormGroup; private lastloginname = ''; - private testEndButtonText = 'Test beenden'; private bookletSelectPrompt = 'Bitte wählen'; private bookletSelectTitle = 'Bitte wählen'; - private codeInputPrompt = ''; - private codeInputTitle = ''; // ?? // private sessiondata: PersonBooklets; @@ -44,7 +41,7 @@ export class StartComponent implements OnInit, OnDestroy { constructor(private fb: FormBuilder, - private mds: MainDataService, + public mds: MainDataService, public messsageDialog: MatDialog, private router: Router, private bs: BackendService) { @@ -110,8 +107,6 @@ export class StartComponent implements OnInit, OnDestroy { } else { // code not yet given // code prompt - this.codeInputTitle = this.mds.getSysConfigValue(SysConfigKey.codeInputTitle); - this.codeInputPrompt = this.mds.getSysConfigValue(SysConfigKey.codeInputPrompt); this.showCodeForm = true; this.showBookletButtons = false; } @@ -148,10 +143,8 @@ export class StartComponent implements OnInit, OnDestroy { this.bookletSelectPrompt = (allOk.length > 1) ? 'Testhefte beendet' : 'Testheft beendet'; this.bookletSelectTitle = 'Beendet'; } else if (numberOfOpenBooklets === 1) { - this.bookletSelectPrompt = this.mds.getSysConfigValue(SysConfigKey.bookletSelectPromptOne); this.bookletSelectTitle = 'Bitte starten'; } else { - this.bookletSelectPrompt = this.mds.getSysConfigValue(SysConfigKey.bookletSelectPromptMany); this.bookletSelectTitle = 'Bitte wählen'; } }); @@ -192,6 +185,7 @@ export class StartComponent implements OnInit, OnDestroy { // no change in other data } else { this.mds.globalErrorMsg$.next(null); + this.mds.setCostumTextsLogin(loginData.costumTexts); this.mds.setNewLoginData(loginData); } this.dataLoading = false; @@ -206,8 +200,8 @@ export class StartComponent implements OnInit, OnDestroy { this.messsageDialog.open(MessageDialogComponent, { width: '400px', data: <MessageDialogData>{ - title: 'Eingabe Personen-Code: Leer', - content: this.codeInputPrompt, + title: this.mds.getCostumText('login_codeInputTitle') + ': Leer', + content: this.mds.getCostumText('login_codeInputPrompt'), type: MessageType.error } }); @@ -215,8 +209,8 @@ export class StartComponent implements OnInit, OnDestroy { this.messsageDialog.open(MessageDialogComponent, { width: '400px', data: <MessageDialogData>{ - title: 'Eingabe Personen-Code: Ungültig', - content: this.codeInputPrompt, + title: this.mds.getCostumText('login_codeInputTitle') + ': Ungültig', + content: this.mds.getCostumText('login_codeInputPrompt'), type: MessageType.error } }); @@ -250,6 +244,7 @@ export class StartComponent implements OnInit, OnDestroy { // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # resetLogin() { + this.mds.setCostumTextsLogin(); this.mds.setNewLoginData(); } diff --git a/src/app/sys-check/questionnaire/questionnaire.component.html b/src/app/sys-check/questionnaire/questionnaire.component.html index 480de7f9b4547ff76bd4846c1bf48d55e0688066..2e0e1828a8c41227feff37f595889afd45b73c2a 100644 --- a/src/app/sys-check/questionnaire/questionnaire.component.html +++ b/src/app/sys-check/questionnaire/questionnaire.component.html @@ -1,6 +1,3 @@ -<div class="spinner-container" *ngIf="dataLoading"> - <mat-spinner></mat-spinner> -</div> <div class="step-body" #questionnaireBody> <div [formGroup]="form" class="formList" fxLayout="column"> <div *ngFor="let q of questions"> diff --git a/src/app/sys-check/report/report.component.html b/src/app/sys-check/report/report.component.html index 6f6b7c68bb0e3f2d0ad127bb17bfd21d78e93326..051dc06db92e9063c9e3cd84fa232dbca000a66c 100644 --- a/src/app/sys-check/report/report.component.html +++ b/src/app/sys-check/report/report.component.html @@ -1,6 +1,3 @@ -<div class="spinner-container" *ngIf="dataLoading"> - <mat-spinner></mat-spinner> -</div> <div class="step-body"> <h2>Computer-Info</h2> <p *ngFor="let rd of environmentData">{{ rd.label }}: {{ rd.value }}</p> diff --git a/src/app/sys-check/run.component.html b/src/app/sys-check/run.component.html index a59d4804534b1c607766c6ff1f9a1fe846fb6ee4..020fe22761c4134e45d67e9e94415c123d0b9bf7 100644 --- a/src/app/sys-check/run.component.html +++ b/src/app/sys-check/run.component.html @@ -4,9 +4,6 @@ </a> </div> <div class="pagetitle">{{ pagetitle }}</div> -<div class="spinner-container" *ngIf="dataLoading"> - <mat-spinner></mat-spinner> -</div> <tc-navi-buttons></tc-navi-buttons> <div class="page-body"> <div class="sheetofpaper"> diff --git a/src/app/sys-check/start.component.ts b/src/app/sys-check/start.component.ts index 7c376ae05aca8c317d733cb21be9c7384804c895..b0bd20c4d9df0c2ef75f75998074d65086d3bf08 100644 --- a/src/app/sys-check/start.component.ts +++ b/src/app/sys-check/start.component.ts @@ -12,7 +12,7 @@ import { Component, OnInit } from '@angular/core'; export class StartComponent implements OnInit { checkConfigList: CheckConfig[] = []; - private dataLoading = false; + public dataLoading = false; constructor( private bs: BackendService, diff --git a/src/app/sys-check/unit-check/tc-navi-buttons/tc-navi-buttons.component.ts b/src/app/sys-check/unit-check/tc-navi-buttons/tc-navi-buttons.component.ts index 687a4fe43fe20c791b2930d6a5c1afb1e2629637..0ab43c27e7d486efc3e8154239565879b2325646 100644 --- a/src/app/sys-check/unit-check/tc-navi-buttons/tc-navi-buttons.component.ts +++ b/src/app/sys-check/unit-check/tc-navi-buttons/tc-navi-buttons.component.ts @@ -7,7 +7,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./tc-navi-buttons.component.css'] }) export class TcNaviButtonsComponent { - private showPageNaviButtons = true; + public showPageNaviButtons = true; private pagePrevEnabled = false; private pageNextEnabled = false; diff --git a/src/app/sys-check/unit-check/unit-check.component.ts b/src/app/sys-check/unit-check/unit-check.component.ts index e7577dc9f70e0bfe38c0fc3addb3c8b25ae3d230..35017696d2d6cf560815436bfbe6714b2693047d 100644 --- a/src/app/sys-check/unit-check/unit-check.component.ts +++ b/src/app/sys-check/unit-check/unit-check.component.ts @@ -33,7 +33,7 @@ export class UnitCheckComponent implements OnInit, OnDestroy { private pendingItemDefinition$ = new BehaviorSubject(null); - private dataLoading = false; + public dataLoading = false; constructor( private ds: SyscheckDataService, diff --git a/src/app/test-controller/test-controller.classes.ts b/src/app/test-controller/test-controller.classes.ts index a213f4a73a69f08d467dfdaab9858dbaca35f7ca..a9db5cb199fce1df126d3709c572af73b2d110a3 100644 --- a/src/app/test-controller/test-controller.classes.ts +++ b/src/app/test-controller/test-controller.classes.ts @@ -99,7 +99,6 @@ export class TestletContentElement { export class UnitDef extends TestletContentElement { readonly alias: string; readonly naviButtonLabel: string; - readonly reportStatus: boolean; playerId: string; statusResponses: 'no' | 'some' | 'all'; statusPresentation: 'no' | 'partly' | 'full'; @@ -111,13 +110,11 @@ export class UnitDef extends TestletContentElement { id: string, title: string, alias: string, - naviButtonLabel: string, - reportStatus: boolean, + naviButtonLabel: string ) { super(sequenceId, id, title); this.alias = alias; this.naviButtonLabel = naviButtonLabel; - this.reportStatus = reportStatus; this.statusResponses = 'no'; this.statusPresentation = 'no'; } @@ -182,9 +179,8 @@ export class Testlet extends TestletContentElement { id: string, title: string, alias: string, - naviButtonLabel: string, - reportStatus: boolean): UnitDef { - const newChild = new UnitDef(sequenceId, id, title, alias, naviButtonLabel, reportStatus); + naviButtonLabel: string): UnitDef { + const newChild = new UnitDef(sequenceId, id, title, alias, naviButtonLabel); this.children.push(newChild); return newChild; } diff --git a/src/app/test-controller/test-controller.component.ts b/src/app/test-controller/test-controller.component.ts index ed5c1de90449426b070490c8540bd3ef4dddad44..0582b199f45b3e30ee3d283207a8e5b6f7f98b9e 100644 --- a/src/app/test-controller/test-controller.component.ts +++ b/src/app/test-controller/test-controller.component.ts @@ -22,7 +22,9 @@ export class TestControllerComponent implements OnInit, OnDestroy { private navigationRequestSubsription: Subscription = null; private maxTimerSubscription: Subscription = null; - private dataLoading = false; + public dataLoading = false; + public showProgress = true; + private lastUnitSequenceId = 0; private lastTestletIndex = 0; private timerValue: MaxTimerData = null; @@ -30,19 +32,26 @@ export class TestControllerComponent implements OnInit, OnDestroy { private allUnitIds: string[] = []; private progressValue = 0; private loadedUnitCount = 0; - private showProgress = true; constructor ( - private tcs: TestControllerService, - private reviewDialog: MatDialog, - private bs: BackendService, private mds: MainDataService, + public tcs: TestControllerService, + private bs: BackendService, + private reviewDialog: MatDialog, private snackBar: MatSnackBar, private router: Router ) { // this.unitPosSubsription = this.tcs.currentUnitPos$.subscribe(u => this.updateStatus()); } + private getCostumText(key: string): string { + const value = this.tcs.getCostumText(key); + if (value.length > 0) { + return value; + } else { + return this.mds.getCostumText(key); + } + } // '''''''''''''''''''''''''''''''''''''''''''''''''''' // private: recursive reading testlets/units from xml // '''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -94,20 +103,6 @@ export class TestControllerComponent implements OnInit, OnDestroy { for (let childIndex = 0; childIndex < childElements.length; childIndex++) { if (childElements[childIndex].nodeName === 'Unit') { - let reportstatus: string = childElements[childIndex].getAttribute('reportStatus'); - if ((typeof reportstatus !== 'undefined') && (reportstatus !== null)) { - if (reportstatus.length > 0) { - reportstatus = reportstatus.substr(0, 1).toLowerCase(); - if ((reportstatus === 'y') || (reportstatus === 'j')) { - reportstatus = 't'; - } - } else { - reportstatus = 'n'; - } - } else { - reportstatus = 'n'; - } - const myUnitId = childElements[childIndex].getAttribute('id'); let myUnitAlias = childElements[childIndex].getAttribute('alias'); if (!myUnitAlias) { @@ -123,7 +118,7 @@ export class TestControllerComponent implements OnInit, OnDestroy { const newUnit = targetTestlet.addUnit(this.lastUnitSequenceId, myUnitId, childElements[childIndex].getAttribute('label'), myUnitAliasClear, - childElements[childIndex].getAttribute('labelshort'), reportstatus === 't'); + childElements[childIndex].getAttribute('labelshort')); this.lastUnitSequenceId += 1; } else if (childElements[childIndex].nodeName === 'Testlet') { @@ -163,6 +158,21 @@ export class TestControllerComponent implements OnInit, OnDestroy { const unitsElements = oDOM.documentElement.getElementsByTagName('Units'); if (unitsElements.length > 0) { + const costumTextsElements = oDOM.documentElement.getElementsByTagName('CostumTexts'); + if (costumTextsElements.length > 0) { + const costumTexts = costumTextsElements[0].children; + const costumTextsForBooklet = {}; + for (let childIndex = 0; childIndex < costumTexts.length; childIndex++) { + if (costumTexts[childIndex].nodeName === 'Text') { + const costumTextKey = costumTexts[childIndex].getAttribute('key'); + if ((typeof costumTextKey !== 'undefined') && (costumTextKey !== null)) { + costumTextsForBooklet[costumTextKey] = costumTexts[childIndex].textContent; + } + } + } + this.tcs.setCostumTexts(costumTextsForBooklet); + } + const bookletConfigElements = oDOM.documentElement.getElementsByTagName('BookletConfig'); if (bookletConfigElements.length > 0) { const bookletConfigs = bookletConfigElements[0].children; @@ -181,6 +191,13 @@ export class TestControllerComponent implements OnInit, OnDestroy { this.tcs.showNavButtons = true; } } + } else if (bookletConfigs[childIndex].nodeName === 'LockOnlyIfResponsesComplete') { + const configParameter = bookletConfigs[childIndex].getAttribute('parameter'); + if ((typeof configParameter !== 'undefined') && (configParameter !== null)) { + if (configParameter === '1') { + this.tcs.LockOnlyIfResponsesComplete = true; + } + } } } } @@ -251,7 +268,7 @@ export class TestControllerComponent implements OnInit, OnDestroy { if (myUnitData.restorepoint) { this.tcs.newUnitRestorePoint(myUnit.id, sequenceId, JSON.parse(myUnitData.restorepoint), false); } - let playerId = ''; + let playerId = null; let definitionRef = ''; try { @@ -278,12 +295,12 @@ export class TestControllerComponent implements OnInit, OnDestroy { } } catch (error) { console.log('error parsing xml for unit "' + myUnit.id + '": ' + error.toString()); - playerId = ''; + playerId = null; definitionRef = ''; } this.incrementProgressValueBy1(); - if (playerId.length > 0) { + if (playerId) { myUnit.playerId = playerId; return this.loadPlayerOk(playerId).pipe( @@ -323,10 +340,10 @@ export class TestControllerComponent implements OnInit, OnDestroy { this.maxTimerSubscription = this.tcs.maxTimeTimer$.subscribe(maxTimerData => { if (maxTimerData.type === MaxTimerDataType.STARTED) { - this.snackBar.open('Bearbeitungszeit hat begonnen: ' + maxTimerData.timeLeftMinString, '', {duration: 3000}); + this.snackBar.open(this.getCostumText('booklet_msgTimerStarted') + maxTimerData.timeLeftMinString, '', {duration: 3000}); this.timerValue = maxTimerData; } else if (maxTimerData.type === MaxTimerDataType.ENDED) { - this.snackBar.open('Bearbeitungszeit beendet', '', {duration: 3000}); + this.snackBar.open(this.getCostumText('booklet_msgTimeOver'), '', {duration: 3000}); this.tcs.rootTestlet.setTimeLeftNull(maxTimerData.testletId); this.tcs.LastMaxTimerState[maxTimerData.testletId] = 0; this.tcs.setBookletState(LastStateKey.MAXTIMELEFT, JSON.stringify(this.tcs.LastMaxTimerState)); @@ -336,7 +353,7 @@ export class TestControllerComponent implements OnInit, OnDestroy { this.tcs.setUnitNavigationRequest('#next'); } } else if (maxTimerData.type === MaxTimerDataType.CANCELLED) { - this.snackBar.open('Bearbeitungszeit abgebrochen', '', {duration: 3000}); + this.snackBar.open(this.getCostumText('booklet_msgTimerCancelled'), '', {duration: 3000}); this.tcs.rootTestlet.setTimeLeftNull(maxTimerData.testletId); this.tcs.LastMaxTimerState[maxTimerData.testletId] = 0; this.tcs.setBookletState(LastStateKey.MAXTIMELEFT, JSON.stringify(this.tcs.LastMaxTimerState)); @@ -348,9 +365,9 @@ export class TestControllerComponent implements OnInit, OnDestroy { this.tcs.setBookletState(LastStateKey.MAXTIMELEFT, JSON.stringify(this.tcs.LastMaxTimerState)); } if ((maxTimerData.timeLeftSeconds / 60) === 5) { - this.snackBar.open('Bearbeitungszeit noch ca. 5 min', '', {duration: 3000}); + this.snackBar.open(this.getCostumText('booklet_msgSoonTimeOver5Minutes'), '', {duration: 3000}); } else if ((maxTimerData.timeLeftSeconds / 60) === 1) { - this.snackBar.open('Bearbeitungszeit noch ca. 1 min', '', {duration: 3000}); + this.snackBar.open(this.getCostumText('booklet_msgSoonTimeOver1Minute'), '', {duration: 3000}); } } }); @@ -372,7 +389,6 @@ export class TestControllerComponent implements OnInit, OnDestroy { startWith = this.tcs.minUnitSequenceId - 1; } const nextUnitSequenceId = this.tcs.rootTestlet.getNextUnlockedUnitSequenceId(startWith); - console.log('getNextUnlockedUnitSequenceId: ' + nextUnitSequenceId.toString()); if (nextUnitSequenceId > 0) { this.router.navigateByUrl('/t/u/' + (nextUnitSequenceId).toString()); } diff --git a/src/app/test-controller/test-controller.interfaces.ts b/src/app/test-controller/test-controller.interfaces.ts index dd66d041032b73996b6970c84d13b752cc283543..512acc44fdea760b2bbba9866090a1ccbebde0bc 100644 --- a/src/app/test-controller/test-controller.interfaces.ts +++ b/src/app/test-controller/test-controller.interfaces.ts @@ -18,6 +18,7 @@ export interface UnitRestorePointData { // testcontroller restrictions +++++++++++++++++++++++++++++++++++ export interface StartLockData { title: string; + prompt: string; codes: CodeInputData[]; } diff --git a/src/app/test-controller/test-controller.service.ts b/src/app/test-controller/test-controller.service.ts index bc8ca035c20c15a59ab85969d4b6f45a2f91e7d3..53a69e34b57bc5bdeaf3e512abc49aea6522ac1b 100644 --- a/src/app/test-controller/test-controller.service.ts +++ b/src/app/test-controller/test-controller.service.ts @@ -36,6 +36,7 @@ export class TestControllerService { public unitListForNaviButtons$ = new BehaviorSubject<UnitNaviButtonData[]>([]); public navPolicyNextOnlyIfPresentationComplete = false; public showNavButtons = false; + public LockOnlyIfResponsesComplete = false; public get currentUnitSequenceId(): number { return this._currentUnitSequenceId; @@ -72,6 +73,7 @@ export class TestControllerService { private restorePointsToSave$ = new Subject<UnitRestorePointData>(); private responsesToSave$ = new Subject<UnitResponseData>(); + private _costumTexts: KeyValuePair = {}; constructor ( private bs: BackendService @@ -125,6 +127,7 @@ export class TestControllerService { this.unitListForNaviButtons$.next([]); this.navPolicyNextOnlyIfPresentationComplete = false; this.showNavButtons = false; + this._costumTexts = {}; } // 7777777777777777777777777777777777777777777777777777777777777777777777 @@ -297,7 +300,19 @@ export class TestControllerService { if (this.rootTestlet) { this.minUnitSequenceId = this.rootTestlet.getFirstUnlockedUnitSequenceId(startWith); this.maxUnitSequenceId = this.rootTestlet.getLastUnlockedUnitSequenceId(startWith); - console.log('updateMinMaxUnitSequenceId: ' + this.minUnitSequenceId.toString() + '/' + this.maxUnitSequenceId.toString()); } } + + // 7777777777777777777777777777777777777777777777777777777777777777777777 + public setCostumTexts(sc: KeyValuePair = {}) { + this._costumTexts = sc; + } + public getCostumText(key: string): string { + if (this._costumTexts) { + if (this._costumTexts.hasOwnProperty(key)) { + return this._costumTexts[key]; + } + } + return ''; + } } diff --git a/src/app/test-controller/unithost/unit-routing-guards.ts b/src/app/test-controller/unithost/unit-routing-guards.ts index 5110ceb728604616874083881f75e048cc426ea2..9221949c1708834c896beb09b72375d96a3eb1db 100644 --- a/src/app/test-controller/unithost/unit-routing-guards.ts +++ b/src/app/test-controller/unithost/unit-routing-guards.ts @@ -1,4 +1,3 @@ -import { FormGroup } from '@angular/forms'; import { StartLockInputComponent } from '../start-lock-input/start-lock-input.component'; import { ConfirmDialogComponent, ConfirmDialogData } from '../../iqb-common/confirm-dialog/confirm-dialog.component'; import { MatDialog, MatSnackBar } from '@angular/material'; @@ -6,20 +5,31 @@ import { TestControllerService } from '../test-controller.service'; import { switchMap, map } from 'rxjs/operators'; import { UnithostComponent } from './unithost.component'; import { Injectable } from '@angular/core'; -import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, Resolve } from '@angular/router'; +import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable, of } from 'rxjs'; -import { UnitDef, UnitControllerData, Testlet } from '../test-controller.classes'; -import { CodeInputData, LogEntryKey, StartLockData, KeyValuePair, LastStateKey } from '../test-controller.interfaces'; +import { UnitControllerData } from '../test-controller.classes'; +import { CodeInputData, LogEntryKey, StartLockData } from '../test-controller.interfaces'; +import { MainDataService } from 'src/app/maindata.service'; @Injectable() export class UnitActivateGuard implements CanActivate { constructor( private tcs: TestControllerService, + private mds: MainDataService, public startLockDialog: MatDialog, public confirmDialog: MatDialog, private snackBar: MatSnackBar ) {} + private getCostumText(key: string): string { + const value = this.tcs.getCostumText(key); + if (value.length > 0) { + return value; + } else { + return this.mds.getCostumText(key); + } + } + // **************************************************************************************** checkAndSolve_PresentationCompleteCode(newUnit: UnitControllerData): Observable<Boolean> { let checkPC = this.tcs.navPolicyNextOnlyIfPresentationComplete && this.tcs.currentUnitSequenceId > 0; @@ -38,16 +48,16 @@ export class UnitActivateGuard implements CanActivate { width: '500px', // height: '300px', data: <ConfirmDialogData>{ - title: 'Weiterblättern nicht möglich', - content: 'Bitte warte das Abspielen des Audiotextes ab bzw. schau dir alle Seiten vollständig an! ' + - 'Erst dann kannst Du weiterblättern.', + title: this.getCostumText('booklet_msgPresentationNotCompleteTitle'), + content: this.getCostumText('booklet_msgPresentationNotCompleteText'), confirmbuttonlabel: 'OK', confirmbuttonreturn: false } }); return dialogCDRef.afterClosed().pipe(map(ok => false)); } else { - this.snackBar.open('Im Hot-Modus dürfte hier nicht weitergeblättert werden.', 'Weiterblättern', {duration: 3000}); + this.snackBar.open('Im Hot-Modus dürfte hier nicht weitergeblättert werden (PresentationNotComplete).', + 'Weiterblättern', {duration: 3000}); return of(true); } } @@ -57,16 +67,16 @@ export class UnitActivateGuard implements CanActivate { width: '500px', // height: '300px', data: <ConfirmDialogData>{ - title: 'Weiterblättern nicht möglich', - content: 'Bitte warte das Abspielen des Audiotextes ab bzw. schau dir alle Seiten vollständig an! ' + - 'Erst dann kannst Du weiterblättern.', + title: this.getCostumText('booklet_msgPresentationNotCompleteTitle'), + content: this.getCostumText('booklet_msgPresentationNotCompleteText'), confirmbuttonlabel: 'OK', confirmbuttonreturn: false } }); return dialogCDRef.afterClosed().pipe(map(ok => false)); } else { - this.snackBar.open('Im Hot-Modus dürfte hier nicht weitergeblättert werden.', 'Weiterblättern', {duration: 3000}); + this.snackBar.open('Im Hot-Modus dürfte hier nicht weitergeblättert werden (PresentationNotComplete).', + 'Weiterblättern', {duration: 3000}); return of(true); } } @@ -92,7 +102,8 @@ export class UnitActivateGuard implements CanActivate { const dialogRef = this.startLockDialog.open(StartLockInputComponent, { width: '500px', data: <StartLockData>{ - title: 'Freigabecodes', + title: this.getCostumText('booklet_codeToEnterTitle'), + prompt: this.getCostumText('booklet_codeToEnterPrompt'), codes: myCodes } }); @@ -117,7 +128,7 @@ export class UnitActivateGuard implements CanActivate { return of(true); } else { - this.snackBar.open('Die Eingabe war nicht korrekt.', 'Freigabewort', {duration: 3000}); + this.snackBar.open('Die Eingabe war nicht korrekt.', this.getCostumText('booklet_codeToEnterTitle'), {duration: 3000}); return of(false); } } @@ -146,8 +157,8 @@ export class UnitActivateGuard implements CanActivate { width: '500px', // height: '300px', data: <ConfirmDialogData>{ - title: 'Aufgabenbereich verlassen?', - content: 'Wenn du jetzt weiterblätterst, ist die Bearbeitungszeit beendet und du kannst nicht zurück.', + title: this.getCostumText('booklet_warningLeaveTimerBlockTitle'), + content: this.getCostumText('booklet_warningLeaveTimerBlockPrompt'), confirmbuttonlabel: 'Trotzdem weiter', confirmbuttonreturn: true } @@ -187,9 +198,8 @@ export class UnitActivateGuard implements CanActivate { width: '500px', // height: '300px', data: <ConfirmDialogData>{ - title: 'Aufgabenbereich verlassen?', - content: 'Wenn du jetzt weiterblätterst, ist die Bearbeitungszeit des vorherigen Aufgabenbereiches' + - ' beendet und du kannst nicht zurück.', + title: this.getCostumText('booklet_warningLeaveTimerBlockTitle'), + content: this.getCostumText('booklet_warningLeaveTimerBlockTextPrompt'), confirmbuttonlabel: 'Trotzdem weiter', confirmbuttonreturn: true } diff --git a/src/app/test-controller/unithost/unithost.component.html b/src/app/test-controller/unithost/unithost.component.html index f3a8677407a602041cb0432ad4eb47665bf8f795..cb22160a00eeaf5c3007948ca50f52c7814d00c4 100644 --- a/src/app/test-controller/unithost/unithost.component.html +++ b/src/app/test-controller/unithost/unithost.component.html @@ -1,6 +1,3 @@ -<div class="spinner-container" *ngIf="dataLoading"> - <mat-spinner></mat-spinner> -</div> <div id="unit-title" fxLayoutAlign="center center"> {{ unitTitle }} </div> diff --git a/src/app/test-controller/unithost/unithost.component.ts b/src/app/test-controller/unithost/unithost.component.ts index e7ca20c74673ca0216029895a6354b7105fefe5a..6d19f7d59228a3007f49e5917045487aa5789804 100644 --- a/src/app/test-controller/unithost/unithost.component.ts +++ b/src/app/test-controller/unithost/unithost.component.ts @@ -22,9 +22,11 @@ export class UnithostComponent implements OnInit, OnDestroy { public leaveWarningText = 'Du hast den Hörtext noch nicht vollständig gehört. Nach dem ' + 'Verlassen der Aufgabe wird der Hörtext nicht noch einmal gestartet. Trotzdem die Aufgabe verlassen?'; + public unitTitle = ''; + public showPageNav = false; + private myUnitSequenceId = -1; private myUnitDbKey = ''; - private unitTitle = ''; // ::::::::::::::::::::: private postMessageSubscription: Subscription = null; @@ -35,7 +37,6 @@ export class UnithostComponent implements OnInit, OnDestroy { private itemplayerValidPages: string[] = []; private itemplayerCurrentPage = ''; - private showPageNav = false; private pageList: PageData[] = []; diff --git a/src/environments/environment.build.ts b/src/environments/environment.build.ts index 4689fb3d77542bbe7ac803cc9e0b1f69f1daa622..cbe7d0cb085a7df39d16c0255707654890aac2b2 100644 --- a/src/environments/environment.build.ts +++ b/src/environments/environment.build.ts @@ -5,5 +5,5 @@ export const environment = { testcenterUrl: '/', appName: 'IQB-Testcenter', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', - appVersion: '0.18.1 - 19.3.2019' + appVersion: '1.0 - 24.3.2019' }; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3612073bc31cd4c1f5d6cbb00318521e9a61bd8a..680a2432afd6b0480b2edcba099e786cb7285a28 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,9 @@ +// ng build --prod + export const environment = { - production: true + production: true, + testcenterUrl: '/', + appName: 'IQB-Testcenter', + appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', + appVersion: '1.0 - 24.3.2019' }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 50ce032506acf3e19931c5eb9d37c03ae74b6cbf..f3f9076c646351f6fd2da2c3cd5fa5f04d871922 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,6 +5,7 @@ export const environment = { production: false, testcenterUrl: 'https://www.iqb-testcenter.de/', + // testcenterUrl: 'https://itemdb2.iqb.hu-berlin.de/', appName: 'IQB-Testcenter', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', appVersion: '0 (dev)'