From 1c63d9fe49c888bdd9bf2638d1fa828568c9aa88 Mon Sep 17 00:00:00 2001 From: Martin Mechtel <mechtelm@user.hu-berlin.de> Date: Mon, 17 Sep 2018 20:19:52 +0200 Subject: [PATCH] 1st after recoding: startpage works --- package-lock.json | 22 +- package.json | 7 +- .../about-dialog/about-dialog.component.css | 0 .../about-dialog/about-dialog.component.html | 15 - .../about-dialog.component.spec.ts | 25 - .../about-dialog/about-dialog.component.ts | 19 - src/app/about/about.component.css | 4 + src/app/about/about.component.html | 46 ++ .../about.component.spec.ts} | 12 +- src/app/about/about.component.ts | 20 + src/app/app-routing.module.ts | 10 +- src/app/app.component.html | 28 +- src/app/app.component.ts | 63 +-- src/app/app.module.ts | 26 +- src/app/{shared => }/backend.service.spec.ts | 0 src/app/backend.service.ts | 218 ++++++++ src/app/home/home.component.css | 14 - src/app/home/home.component.html | 29 -- src/app/home/home.component.ts | 51 -- src/app/iqb-files/index.ts | 3 - src/app/iqb-files/iqb-files.module.ts | 33 -- src/app/iqb-files/iqbFile.scss | 61 --- .../iqbFileUpload.component.html | 13 - .../iqbFileUpload/iqbFileUpload.component.ts | 191 ------- .../iqbFileUploadInputFor.directive.spec.ts | 0 .../iqbFileUploadInputFor.directive.ts | 59 --- .../iqbFileUploadQueue.component.html | 18 - .../iqbFileUploadQueue.component.spec.ts | 0 .../iqbFileUploadQueue.component.ts | 118 ----- src/app/logindata.service.spec.ts | 15 + src/app/logindata.service.ts | 82 +++ src/app/shared/backend.service.ts | 142 ------ src/app/shared/global-store.service.spec.ts | 15 - src/app/shared/global-store.service.ts | 28 -- src/app/start/start.component.css | 6 +- src/app/start/start.component.html | 103 +++- src/app/start/start.component.ts | 343 +++++++++---- .../test-controller/backend.service.spec.ts | 15 - src/app/test-controller/backend.service.ts | 225 --------- src/app/test-controller/index.ts | 4 - .../resize-IFrameChild.directive.spec.ts | 0 .../resize-IFrameChild.directive.ts | 30 -- .../test-controller-routing.module.ts | 31 -- .../test-controller.component.spec.ts | 25 - .../test-controller.component.ts | 7 - .../test-controller/test-controller.module.ts | 34 -- .../test-controller/testdata.service.spec.ts | 15 - src/app/test-controller/testdata.service.ts | 468 ------------------ .../unithost/unit-routing.spec.ts | 28 -- .../test-controller/unithost/unit-routing.ts | 56 --- .../unithost/unithost.component.css | 3 - .../unithost/unithost.component.html | 3 - .../unithost/unithost.component.spec.ts | 25 - .../unithost/unithost.component.ts | 77 --- src/environments/environment.ts | 2 +- 55 files changed, 789 insertions(+), 2098 deletions(-) delete mode 100644 src/app/about-dialog/about-dialog.component.css delete mode 100644 src/app/about-dialog/about-dialog.component.html delete mode 100644 src/app/about-dialog/about-dialog.component.spec.ts delete mode 100644 src/app/about-dialog/about-dialog.component.ts create mode 100644 src/app/about/about.component.css create mode 100644 src/app/about/about.component.html rename src/app/{home/home.component.spec.ts => about/about.component.spec.ts} (57%) create mode 100644 src/app/about/about.component.ts rename src/app/{shared => }/backend.service.spec.ts (100%) create mode 100644 src/app/backend.service.ts delete mode 100644 src/app/home/home.component.css delete mode 100644 src/app/home/home.component.html delete mode 100644 src/app/home/home.component.ts delete mode 100644 src/app/iqb-files/index.ts delete mode 100644 src/app/iqb-files/iqb-files.module.ts delete mode 100644 src/app/iqb-files/iqbFile.scss delete mode 100644 src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.html delete mode 100644 src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.ts delete mode 100644 src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.spec.ts delete mode 100644 src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.ts delete mode 100644 src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.html delete mode 100644 src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.spec.ts delete mode 100644 src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.ts create mode 100644 src/app/logindata.service.spec.ts create mode 100644 src/app/logindata.service.ts delete mode 100644 src/app/shared/backend.service.ts delete mode 100644 src/app/shared/global-store.service.spec.ts delete mode 100644 src/app/shared/global-store.service.ts delete mode 100644 src/app/test-controller/backend.service.spec.ts delete mode 100644 src/app/test-controller/backend.service.ts delete mode 100644 src/app/test-controller/index.ts delete mode 100644 src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.spec.ts delete mode 100644 src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.ts delete mode 100644 src/app/test-controller/test-controller-routing.module.ts delete mode 100644 src/app/test-controller/test-controller.component.spec.ts delete mode 100644 src/app/test-controller/test-controller.component.ts delete mode 100644 src/app/test-controller/test-controller.module.ts delete mode 100644 src/app/test-controller/testdata.service.spec.ts delete mode 100644 src/app/test-controller/testdata.service.ts delete mode 100644 src/app/test-controller/unithost/unit-routing.spec.ts delete mode 100644 src/app/test-controller/unithost/unit-routing.ts delete mode 100644 src/app/test-controller/unithost/unithost.component.css delete mode 100644 src/app/test-controller/unithost/unithost.component.html delete mode 100644 src/app/test-controller/unithost/unithost.component.spec.ts delete mode 100644 src/app/test-controller/unithost/unithost.component.ts diff --git a/package-lock.json b/package-lock.json index 007da886..39703228 100644 --- a/package-lock.json +++ b/package-lock.json @@ -337,6 +337,14 @@ "tslib": "^1.9.0" } }, + "@angular/flex-layout": { + "version": "6.0.0-beta.18", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-6.0.0-beta.18.tgz", + "integrity": "sha512-1Alv3YSIZYp0CTUIESIaSQLoSVyLzuNKPa5bGM/RzOmeSrndm5plVgI9wopGfJUDiwM18R97rq/4XjDvNT/+ig==", + "requires": { + "tslib": "^1.7.1" + } + }, "@angular/forms": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.2.tgz", @@ -420,6 +428,14 @@ "@angular-devkit/core": "0.6.8", "@angular-devkit/schematics": "0.6.8", "typescript": ">=2.6.2 <2.8" + }, + "dependencies": { + "typescript": { + "version": "2.7.2", + "resolved": "http://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", + "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "dev": true + } } }, "@schematics/update": { @@ -9627,9 +9643,9 @@ "dev": true }, "typescript": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", - "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index fd9ecc4d..4a206b4b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@angular/common": "^6.0.3", "@angular/compiler": "^6.0.3", "@angular/core": "^6.0.3", + "@angular/flex-layout": "^6.0.0-beta.18", "@angular/forms": "^6.0.3", "@angular/http": "^6.0.3", "@angular/material": "^6.4.3", @@ -27,10 +28,9 @@ "zone.js": "^0.8.26" }, "devDependencies": { - "@angular/compiler-cli": "^6.0.3", "@angular-devkit/build-angular": "~0.6.8", - "typescript": "~2.7.2", "@angular/cli": "~6.0.8", + "@angular/compiler-cli": "^6.0.3", "@angular/language-service": "^6.0.3", "@types/jasmine": "~2.8.6", "@types/jasminewd2": "~2.0.3", @@ -45,6 +45,7 @@ "karma-jasmine-html-reporter": "^0.2.2", "protractor": "~5.3.0", "ts-node": "~5.0.1", - "tslint": "~5.9.1" + "tslint": "~5.9.1", + "typescript": "^2.9.2" } } diff --git a/src/app/about-dialog/about-dialog.component.css b/src/app/about-dialog/about-dialog.component.css deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/about-dialog/about-dialog.component.html b/src/app/about-dialog/about-dialog.component.html deleted file mode 100644 index d1db4b07..00000000 --- a/src/app/about-dialog/about-dialog.component.html +++ /dev/null @@ -1,15 +0,0 @@ -<h1 mat-dialog-title>Programm-Info</h1> -<mat-dialog-content> - <ul> - <li>Programmname: {{ appName }}</li> - <li>Programmversion: {{ appVersion }}</li> - <li>Copyright: {{ appPublisher }}</li> - </ul> - <p>Der Zugriff auf den nicht-öffentlichen Bereich ist nur über Kontaktaufnahme mit dem IQB möglich. - Das System befindet sich in einem sehr frühen Entwicklungsstadium. Informationen zur OpenCBA-Initiative - sind auch nur direkt über das IQB erhältlich. - </p> -</mat-dialog-content> -<mat-dialog-actions> - <button mat-raised-button mat-dialog-close>Schließen</button> -</mat-dialog-actions> diff --git a/src/app/about-dialog/about-dialog.component.spec.ts b/src/app/about-dialog/about-dialog.component.spec.ts deleted file mode 100644 index 7fc637d3..00000000 --- a/src/app/about-dialog/about-dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { AboutDialogComponent } from './about-dialog.component'; - -describe('AboutDialogComponent', () => { - let component: AboutDialogComponent; - let fixture: ComponentFixture<AboutDialogComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ AboutDialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AboutDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/about-dialog/about-dialog.component.ts b/src/app/about-dialog/about-dialog.component.ts deleted file mode 100644 index aefd041a..00000000 --- a/src/app/about-dialog/about-dialog.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { GlobalStoreService } from './../shared/global-store.service'; -import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; -import { Component, Inject } from '@angular/core'; - -@Component({ - selector: 'tc-about-dialog', - templateUrl: './about-dialog.component.html', - styleUrls: ['./about-dialog.component.css'] -}) -export class AboutDialogComponent { - - constructor( - @Inject('APP_NAME') private appName: string, - @Inject('APP_PUBLISHER') private appPublisher: string, - @Inject('APP_VERSION') private appVersion: string, - @Inject(MAT_DIALOG_DATA) public data: any, - private gss: GlobalStoreService) { } - -} diff --git a/src/app/about/about.component.css b/src/app/about/about.component.css new file mode 100644 index 00000000..f4269125 --- /dev/null +++ b/src/app/about/about.component.css @@ -0,0 +1,4 @@ +div.intro-main { + margin: 40px; + max-width: 600px; +} diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html new file mode 100644 index 00000000..582dc50d --- /dev/null +++ b/src/app/about/about.component.html @@ -0,0 +1,46 @@ +<div class="page-body"> + <div class="sheetofpaper"> + <div class="intro-main"> + <p>Das <a href="http://www.iqb.hu-berlin.de" target="_blank">Institut zur Qualitätsentwicklung im Bildungswesen</a> + betreibt auf diesen Seiten eine Pilotanwendung für das computerbasierte Leistungstesten von + Schülerinnen und Schülern. Der Zugang zu einem Test ist nur möglich, wenn Sie von Testverantwortlichen + Zugangsdaten erhalten haben. Es sind keine weiteren Seiten öffentlich verfügbar.</p> + + <p>Die mit diesem Testsystem erhobenen Daten sind vertraulich und enthalten grundsätzlich keinen direkten + Personenbezug. Es werden z. B. nie Namen gespeichert. Um Auskünfte zu einer bestimmten Studie + zu erhalten, wenden Sie sich bitte an das <a href="mailto:mechtel@iqb.hu-berlin.de?subject=Zum%20IQB-Testsystem"> + IQB</a>. Wir benötigen dazu den ungefähren Testzeitraum und das Bundesland, in dem die Studie + durchgeführt wurde.</p> + + <!--<tc-statustext></tc-statustext>--> + + <ul> + <li>Programmname: {{ appName }}</li> + <li>Programmversion: {{ appVersion }}</li> + <li>Copyright: {{ appPublisher }}</li> + </ul> + + <p> + <em>Postanschrift:</em><br/> + Humboldt-Universität zu Berlin<br/> + Institut zur Qualitätsentwicklung im Bildungswesen<br/> + Unter den Linden 6<br/> + 10099 Berlin</p> + <p> + <em>Sitz:</em><br/> + Luisenstr. 56<br/> + 10117 Berlin<br/> + Tel: +49 [30] 2093 - 46500 (Zentrale)<br/> + Fax: +49 [30] 2093 - 46599<br/> + E-Mail: <a href="mailto:iqboffice@iqb.hu-berlin.de">iqboffice@iqb.hu-berlin.de</a> + </p> + <p> + <em>Name und Anschrift der Datenschutzbeauftragten</em><br/> + Frau Gesine Hoffmann-Holland<br/> + Tel: +49 (30) 2093-2591<br/> + E-Mail: datenschutz@uv.hu-berlin.de<br/> + <a href="http://www.hu-berlin.de/de/datenschutz" target="_blank">www.hu-berlin.de/de/datenschutz</a> + </p> + </div> + </div> +</div> diff --git a/src/app/home/home.component.spec.ts b/src/app/about/about.component.spec.ts similarity index 57% rename from src/app/home/home.component.spec.ts rename to src/app/about/about.component.spec.ts index 490e81bd..6b773448 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/about/about.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { HomeComponent } from './home.component'; +import { AboutComponent } from './about.component'; -describe('HomeComponent', () => { - let component: HomeComponent; - let fixture: ComponentFixture<HomeComponent>; +describe('AboutComponent', () => { + let component: AboutComponent; + let fixture: ComponentFixture<AboutComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HomeComponent ] + declarations: [ AboutComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(HomeComponent); + fixture = TestBed.createComponent(AboutComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts new file mode 100644 index 00000000..01393470 --- /dev/null +++ b/src/app/about/about.component.ts @@ -0,0 +1,20 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { LogindataService } from '../logindata.service'; + +@Component({ + templateUrl: './about.component.html', + styleUrls: ['./about.component.css'] +}) +export class AboutComponent implements OnInit { + + constructor( + @Inject('APP_NAME') private appName: string, + @Inject('APP_PUBLISHER') private appPublisher: string, + @Inject('APP_VERSION') private appVersion: string, + private lds: LogindataService + ) { } + + ngOnInit() { + this.lds.pageTitle$.next('IQB-Testcenter - Impressum/Datenschutz'); + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c3e58cd0..5aaa3a6a 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,15 +1,15 @@ -import { TestControllerComponent } from './test-controller'; +import { AboutComponent } from './about/about.component'; +// import { TestControllerComponent } from './test-controller'; import { StartComponent } from './start/start.component'; -import { HomeComponent } from './home/home.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ - {path: '', redirectTo: 'home', pathMatch: 'full'}, - {path: 'home', component: HomeComponent}, + {path: '', redirectTo: 'start', pathMatch: 'full'}, {path: 'start', component: StartComponent}, - {path: 't', component: TestControllerComponent} + {path: 'about', component: AboutComponent} +// {path: 't', component: TestControllerComponent} ]; @NgModule({ diff --git a/src/app/app.component.html b/src/app/app.component.html index ae52372d..3a6131bc 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,4 @@ +<!--<tc-navi-buttons></tc-navi-buttons>--> <mat-toolbar color="primary"> <a [routerLink]="['/']"> <img class="logo" src="assets/IQB-LogoA.png" matTooltip="Startseite"/> @@ -5,18 +6,12 @@ <span>{{ title }}</span> <span class="itp-fill-remaining-space"></span> - <button mat-fab [disabled]="!navPrevEnabled" color="accent" (click)="navPrev()"> - <i class="material-icons">chevron_left</i> - </button> - <button mat-fab [disabled]="!navNextEnabled" color="accent" (click)="navNext()"> - <i class="material-icons">chevron_right</i> - </button> <button mat-icon-button [matMenuTriggerFor]="menu"> <i class="material-icons">menu</i> </button> <mat-menu #menu="matMenu"> - <button mat-menu-item (click)="showAboutDialog()"> + <button mat-menu-item [routerLink]="['/about']"> <mat-icon>info</mat-icon> <span>Programm-Info</span> </button> @@ -24,23 +19,8 @@ <mat-icon>home</mat-icon> <span>Anmeldeseite</span> </button> - <button mat-menu-item *ngIf="isSession" [routerLink]="['/start']"> - <mat-icon>trending_up</mat-icon> - <span>Zum Test</span> - </button> - <button mat-menu-item *ngIf="isAdmin" [routerLink]="['/admin/myfiles']"> - <mat-icon>settings</mat-icon> - <span>Testverwaltung</span> - </button> - <button mat-menu-item *ngIf="isAdmin" (click)="logout()"> - <mat-icon>exit_to_app</mat-icon> - <span>Logout</span> - </button> - <button mat-menu-item *ngIf="!isAdmin" (click)="login()"> - <mat-icon>input</mat-icon> - <span>Testverwaltung</span> - </button> - </mat-menu> + <!--<tc-menu-buttons></tc-menu-buttons>--> +</mat-menu> </mat-toolbar> <router-outlet></router-outlet> diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 4e29fc2d..de9d843a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,16 +1,9 @@ -import { environment } from './../environments/environment'; -import { merge , Observable , Observer , Subscriber , Subscription } from 'rxjs'; +import { LogindataService } from './logindata.service'; +import { merge } from 'rxjs'; -import { distinctUntilChanged , switchMap } from 'rxjs/operators'; -import { TestdataService } from './test-controller'; -import { IqbCommonModule, ConfirmDialogComponent, ConfirmDialogData } from './iqb-common'; -import { BackendService } from './shared/backend.service'; +// import { TestdataService } from './test-controller'; import { Router } from '@angular/router'; -import { AboutDialogComponent } from './about-dialog/about-dialog.component'; -import { GlobalStoreService } from './shared/global-store.service'; import { Component, OnInit } from '@angular/core'; -import { MatDialog, MatDialogRef } from '@angular/material'; -import { FormGroup } from '@angular/forms'; @Component({ selector: 'tc-root', @@ -21,47 +14,23 @@ import { FormGroup } from '@angular/forms'; export class AppComponent implements OnInit { public title = ''; - public navPrevEnabled = false; - public navNextEnabled = false; - public isSession = false; constructor ( - private gss: GlobalStoreService, - private tss: TestdataService, - private bsApp: BackendService, - private router: Router, - private bs: BackendService, - public aboutDialog: MatDialog) { } + private lds: LogindataService, + private router: Router) { } ngOnInit() { - this.tss.isSession$.subscribe(is => this.isSession = is); - this.tss.navNextEnabled$.subscribe(is => this.navNextEnabled = is); - this.tss.navPrevEnabled$.subscribe(is => this.navPrevEnabled = is); - - merge( - this.gss.pageTitle$, - this.tss.pageTitle$).subscribe(t => { - this.title = t; - }); - - window.addEventListener('message', (event) => { - this.tss.processMessagePost(event); - }, false); - } - - // ******************************************************************************************************* - showAboutDialog() { - const dialogRef = this.aboutDialog.open(AboutDialogComponent, { - width: '500px', - data: {} + this.lds.pageTitle$.subscribe(t => { + this.title = t; }); - } - - navPrev() { - this.tss.gotoPrevUnit(); - } - - navNext() { - this.tss.gotoNextUnit(); + // merge( + // this.gss.pageTitle$, + // this.tss.pageTitle$).subscribe(t => { + // this.title = t; + // }); + + // window.addEventListener('message', (event) => { + // this.lds.processMessagePost(event); + // }, false); } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3cb3222e..e5e2e459 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,7 +1,4 @@ -import { TestControllerModule } from './test-controller'; -import { IqbCommonModule } from './iqb-common'; -import { GlobalStoreService } from './shared/global-store.service'; -import { BackendService } from './shared/backend.service'; +import { AboutComponent } from './about/about.component'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -11,19 +8,20 @@ import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule, MatCheckboxModule, MatMenuModule, MatTooltipModule, MatToolbarModule, MatIconModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatTabsModule } from '@angular/material'; import { AppRoutingModule } from './app-routing.module'; - import { AppComponent } from './app.component'; -import { HomeComponent } from './home/home.component'; -import { AboutDialogComponent } from './about-dialog/about-dialog.component'; + +// import { TestControllerModule } from './test-controller'; +import { IqbCommonModule } from './iqb-common'; +import { BackendService } from './backend.service'; import { StartComponent } from './start/start.component'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; +import { FlexLayoutModule } from '@angular/flex-layout'; @NgModule({ declarations: [ AppComponent, - HomeComponent, - AboutDialogComponent, StartComponent, + AboutComponent ], imports: [ BrowserModule, @@ -37,17 +35,17 @@ import { LocationStrategy, HashLocationStrategy } from '@angular/common'; MatTooltipModule, MatDialogModule, MatTabsModule, + FlexLayoutModule, ReactiveFormsModule, HttpClientModule, - TestControllerModule, +// TestControllerModule, AppRoutingModule, IqbCommonModule ], - entryComponents: [ - AboutDialogComponent, - ], + // entryComponents: [ + // AboutDialogComponent, + // ], providers: [ - GlobalStoreService, BackendService, { provide: LocationStrategy, diff --git a/src/app/shared/backend.service.spec.ts b/src/app/backend.service.spec.ts similarity index 100% rename from src/app/shared/backend.service.spec.ts rename to src/app/backend.service.spec.ts diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts new file mode 100644 index 00000000..b01044ef --- /dev/null +++ b/src/app/backend.service.ts @@ -0,0 +1,218 @@ +import { Injectable, Inject } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpEvent, HttpErrorResponse } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + + + + + +@Injectable() +export class BackendService { + + constructor( + @Inject('SERVER_URL') private serverUrl: string, + private http: HttpClient) { } + + /* + getStatus(admintoken: string, logintoken: string, sessiontoken: string): Observable<LoginResponseData> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<LoginResponseData>(this.serverUrl + 'getStatus.php', {at: admintoken, lt: logintoken, st: sessiontoken}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } */ + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + login(name: string, password: string): Observable<string | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<string>(this.serverUrl + 'testlogin.php', {n: name, p: password}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + // don't use LoginData.code! + getLoginDataByLoginToken(logintoken: string): Observable<LoginData | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<LoginData>(this.serverUrl + 'getLoginDataByLoginToken.php', {lt: logintoken}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + getLoginDataByPersonToken(persontoken: string): Observable<LoginData | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<LoginData>(this.serverUrl + 'getLoginDataByPersonToken.php', {lt: persontoken}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + getBookletStatusByNameAndPersonToken(persontoken: string, bookletname: string): Observable<BookletStatus | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<BookletStatus>(this.serverUrl + 'getBookletStatusByNameAndPersonToken.php', { + pt: persontoken, b: bookletname}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + getBookletStatusByNameAndLoginToken(logintoken: string, code: string, bookletname: string): Observable<BookletStatus | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<BookletStatus>(this.serverUrl + 'getBookletStatusByNameAndLoginToken.php', { + lt: logintoken, b: bookletname, c: code}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + getBookletStatusByDbId(persontoken: string, bookletid: number): Observable<BookletStatus | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<BookletStatus>(this.serverUrl + 'getBookletStatusByDbId.php', { + pt: persontoken, b: bookletid}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + startBookletByLoginToken(logintoken: string, code: string, bookletFilename: string): Observable<PersonTokenAndBookletId | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<PersonTokenAndBookletId>(this.serverUrl + + 'startBookletByLoginToken.php', {lt: logintoken, c: code, b: bookletFilename}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + startBookletByPersonToken(persontoken: string, bookletFilename: string): Observable<number | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<number>(this.serverUrl + 'startBookletByPersonToken.php', {pt: persontoken, b: bookletFilename}, httpOptions) + .pipe( + catchError(this.handleError) + ); + } + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + 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); + } +} + + +// ############################################################################################# + +// class 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 LoginResponseData { + t: string; + n: string; + ws: string; +} + +export interface BookletData { + name: string; + filename: string; + title: string; +} + +export interface BookletDataList { + [code: string]: BookletData[]; +} + +export interface LoginData { + mode: string; + groupname: string; + loginname: string; + workspaceName: string; + booklets: BookletDataList; + code: string; +} + +export interface BookletStatus { + statusLabel: string; + lastUnit: number; + canStart: boolean; + id: number; + label: string; + name: string; +} + +export interface PersonTokenAndBookletId { + personToken: string; + bookletId: number; +} + diff --git a/src/app/home/home.component.css b/src/app/home/home.component.css deleted file mode 100644 index 25a5f362..00000000 --- a/src/app/home/home.component.css +++ /dev/null @@ -1,14 +0,0 @@ -div.intro-main { - margin: 40px; -} - -form { - margin: 40px; - min-width: 150px; - max-width: 500px; -} - -.full-width { - width: 100%; -} - diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html deleted file mode 100644 index 6f62e7c3..00000000 --- a/src/app/home/home.component.html +++ /dev/null @@ -1,29 +0,0 @@ -<div class="page-body"> - <div class="sheetofpaper"> - <div class="intro-main"> - <p>Das <a href="http://www.iqb.hu-berlin.de" target="_blank">Institut zur Qualitätsentwicklung im Bildungswesen</a> - betreibt auf diesen Seiten eine Pilotanwendung für den Standard OpenCBA. Es handelt sich dabei um eine Initiative - von Institutionen zur Entwicklung eines modularen Systems zum computerbasierten Testen.</p> - <p>Um sich für einen bestimmten Test anzumelden, geben Sie bitte den Namen und das Kennwort für einen Tests ein. - Sie können über das Seitenmenü oben rechts weitere Funktionen aufrufen.</p> - <p *ngIf="isSession">Derzeit sind Sie am System angemeldet. Über das Menü oben rechts können Sie zur Startseite eines Tests gelangen.</p> - <p>Bei Fragen zu diesem System wenden Sie sich bitte an <a href="mailto:mechtel@iqb.hu-berlin.de?subject=Zum%20OpenCBA-Testsystem">Martin Mechtel</a>.</p> - </div> - - - <form [formGroup]="testtakerloginform" (ngSubmit)="testtakerlogin()"> - <mat-form-field class="full-width"> - <input matInput formControlName="testname" placeholder="Testname"> - </mat-form-field> - <mat-form-field class="full-width"> - <input matInput type="password" formControlName="testpw" placeholder="Kennwort" (keyup.enter)="testtakerlogin()"> - </mat-form-field> - <button mat-raised-button type="submit" [disabled]="testtakerloginform.invalid">Test starten</button> - </form> - - <div *ngIf="isError" class="intro-main"> - <p>{{ errorMessage }}</p> - </div> - - </div> -</div> diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts deleted file mode 100644 index 7dff3291..00000000 --- a/src/app/home/home.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { TestdataService } from './../test-controller'; -import { BackendService } from './../shared/backend.service'; -import { Router } from '@angular/router'; -import { GlobalStoreService } from './../shared/global-store.service'; -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { FormGroup, FormBuilder, FormArray, FormControl, Validators } from '@angular/forms'; - - -@Component({ - selector: 'tc-home', - templateUrl: './home.component.html', - styleUrls: ['./home.component.css'] -}) -export class HomeComponent implements OnInit { - testtakerloginform: FormGroup; - isSession: boolean; - isError = false; - errorMessage = ''; - - constructor(private fb: FormBuilder, - private router: Router, - private gss: GlobalStoreService, - private tss: TestdataService, - private bs: BackendService) { } - - ngOnInit() { - this.gss.updatePageTitle('IQB-Testcenter - Willkommen!'); - this.tss.isSession$.subscribe(is => { - this.isSession = is; - }); - - this.testtakerloginform = this.fb.group({ - testname: this.fb.control('', [Validators.required, Validators.minLength(3)]), - testpw: this.fb.control('', [Validators.required, Validators.minLength(3)]) - }); - } - - testtakerlogin() { - this.isError = false; - this.errorMessage = ''; - this.bs.testlogin(this.testtakerloginform.get('testname').value, this.testtakerloginform.get('testpw').value).subscribe( - (loginToken: string) => { - this.gss.loginToken = loginToken; - this.router.navigateByUrl('/start'); - }, (errormsg: string) => { - this.isError = true; - this.errorMessage = errormsg; - } - ); - } -} diff --git a/src/app/iqb-files/index.ts b/src/app/iqb-files/index.ts deleted file mode 100644 index f0228b06..00000000 --- a/src/app/iqb-files/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { IqbFileUploadQueueComponent } from './iqbFileUploadQueue/iqbFileUploadQueue.component'; -export { IqbFileUploadInputForDirective } from './iqbFileUploadInputFor/iqbFileUploadInputFor.directive'; -export { IqbFilesModule } from './iqb-files.module'; diff --git a/src/app/iqb-files/iqb-files.module.ts b/src/app/iqb-files/iqb-files.module.ts deleted file mode 100644 index 75493f34..00000000 --- a/src/app/iqb-files/iqb-files.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { IqbCommonModule } from './../iqb-common'; -import { NgModule } from '@angular/core'; -import { IqbFileUploadComponent } from './iqbFileUpload/iqbFileUpload.component'; -import { IqbFileUploadQueueComponent } from './iqbFileUploadQueue/iqbFileUploadQueue.component'; -import { IqbFileUploadInputForDirective } from './iqbFileUploadInputFor/iqbFileUploadInputFor.directive'; - -import { MatProgressBarModule, MatCardModule, MatButtonModule } from '@angular/material'; -import { MatIconModule } from '@angular/material/icon'; -import { HttpClientModule } from '@angular/common/http'; -import { CommonModule } from '@angular/common'; - - -@NgModule({ - imports: [ - MatButtonModule, - MatProgressBarModule, - MatIconModule, - MatCardModule, - HttpClientModule, - IqbCommonModule, - CommonModule - ], - declarations: [ - IqbFileUploadComponent, - IqbFileUploadQueueComponent, - IqbFileUploadInputForDirective - ], - exports: [ - IqbFileUploadQueueComponent, - IqbFileUploadInputForDirective, - ] -}) -export class IqbFilesModule { } diff --git a/src/app/iqb-files/iqbFile.scss b/src/app/iqb-files/iqbFile.scss deleted file mode 100644 index a7b1fc43..00000000 --- a/src/app/iqb-files/iqbFile.scss +++ /dev/null @@ -1,61 +0,0 @@ - - -.dropzone { - background-color: brown; - width: 100px; - height: 100px; -} - - -mat-card { - padding: 15px; -} - -.example-section { - display: flex; - align-content: center; - align-items: center; - height: 10px; -} - -.file-info, .file-info-error { - font-size: .85rem; -} - -.file-info-error { - color: red; -} - -#drop_zone { - border: 5px solid blue; - width: 200px; - height: 100px; -} - -.action { - cursor: pointer; - outline: none; -} - -a.disabled { - pointer-events: none; -} - -.upload-drop-zone { - height: 200px; - border-width: 2px; - margin-bottom: 20px; -} - -/* skin.css Style*/ -.upload-drop-zone { - color: #ccc; - border-style: dashed; - border-color: #ccc; - line-height: 200px; - text-align: center -} -.upload-drop-zone.drop { - color: #222; - border-color: #222; -} diff --git a/src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.html b/src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.html deleted file mode 100644 index 4307132f..00000000 --- a/src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.html +++ /dev/null @@ -1,13 +0,0 @@ -<mat-card> - <span class="file-info">{{file.name}} ({{file.size | bytes}})</span> - <section *ngIf="status<=1" class="example-section"> - <mat-progress-bar class="example-margin" [value]="progressPercentage"></mat-progress-bar> - <a *ngIf="status == 0"><mat-icon class="action" (click)="upload()">file_upload</mat-icon></a> - <mat-icon class="action" (click)="remove()">cancel</mat-icon> - </section> - <span *ngIf="status == 1" class="file-info">{{progressPercentage}} %</span><br/> - <span *ngIf="status != 1"> - <span *ngIf="status == 3" class="file-info-error">{{statustext}}</span> - <span *ngIf="status != 3" class="file-info">{{statustext}}</span> - </span> -</mat-card> diff --git a/src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.ts b/src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.ts deleted file mode 100644 index f298c65c..00000000 --- a/src/app/iqb-files/iqbFileUpload/iqbFileUpload.component.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { BytesPipe } from './../../iqb-common'; -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, Optional, Inject, forwardRef } from '@angular/core'; -import { HttpClient, HttpEventType, HttpHeaders, HttpParams, - HttpErrorResponse, HttpEvent } from '@angular/common/http'; - - -@Component({ - selector: 'iqb-file-upload', - templateUrl: `./iqbFileUpload.component.html`, - exportAs: 'iqbFileUpload', - host: { - 'class': 'iqb-file-upload', - }, - styleUrls: ['./../iqbFile.scss'], - }) - - export class IqbFileUploadComponent implements OnInit { - - constructor( - private myHttpClient: HttpClient) { } - - // '''''''''''''''''''''''' - private _status: UploadStatus; - get status(): UploadStatus { - return this._status; - } - - set status(newstatus: UploadStatus) { - this._status = newstatus; - this.statusChangedEvent.emit(this); - } - - // '''''''''''''''''''''''' - private requestResponseText: string; - get statustext(): string { - let myreturn = ''; - switch (this._status) { - case UploadStatus.busy: { - myreturn = 'Bitte warten'; - break; - } - case UploadStatus.ready: { - myreturn = 'Bereit'; - break; - } - default: { - myreturn = this.requestResponseText; - break; - } - } - return myreturn; - } - - /* Http request input bindings */ - @Input() - httpUrl = 'http://localhost:8080'; - - @Input() - httpRequestHeaders: HttpHeaders | { - [header: string]: string | string[]; - } = new HttpHeaders().set('Content-Type', 'multipart/form-data'); - - @Input() - httpRequestParams: HttpParams | { - [param: string]: string | string[]; - } = new HttpParams(); - - @Input() - fileAlias = 'file'; - - @Input() - tokenName = ''; - - @Input() - token = ''; - - @Input() - folderName = ''; - - @Input() - folder = ''; - - @Input() - get file(): any { - return this._file; - } - set file(file: any) { - this._file = file; - this._filedate = this._file.lastModified; - this.total = this._file.size; - } - - @Input() - set id(id: number) { - this._id = id; - } - - get id(): number { - return this._id; - } - - @Output() removeFileRequestEvent = new EventEmitter<IqbFileUploadComponent>(); - @Output() statusChangedEvent = new EventEmitter<IqbFileUploadComponent>(); - - private progressPercentage = 0; - public loaded = 0; - private total = 0; - private _file: any; - private _filedate = ''; - private _id: number; - private fileUploadSubscription: any; - - - ngOnInit() { - this._status = UploadStatus.ready; - this.requestResponseText = ''; - } - - // ================================================================== - public upload(): void { - if (this.status === UploadStatus.ready) { - - this.status = UploadStatus.busy; - const formData = new FormData(); - formData.set(this.fileAlias, this._file, this._file.name); - if ((typeof this.tokenName !== 'undefined') && (typeof this.token !== 'undefined')) { - if (this.tokenName.length > 0) { - formData.append(this.tokenName, this.token); - } - } - if ((typeof this.folderName !== 'undefined') && (typeof this.folder !== 'undefined')) { - if (this.folderName.length > 0) { - formData.append(this.folderName, this.folder); - } - } - this.fileUploadSubscription = this.myHttpClient.post(this.httpUrl, formData, { - // headers: this.httpRequestHeaders, - observe: 'events', - params: this.httpRequestParams, - reportProgress: true, - responseType: 'json' - }).subscribe((event: HttpEvent<any>) => { - if (event.type === HttpEventType.UploadProgress) { - this.progressPercentage = Math.floor( event.loaded * 100 / event.total ); - this.loaded = event.loaded; - this.total = event.total; - this.status = UploadStatus.busy; - } else if (event.type === HttpEventType.Response) { - this.requestResponseText = event.body; - if ((this.requestResponseText.length > 5) && (this.requestResponseText.substr(0, 2) === 'e:')) { - this.requestResponseText = this.requestResponseText.substr(2); - this.status = UploadStatus.error; - } else { - this.status = UploadStatus.ok; - } - } - }, (errorObj: HttpErrorResponse) => { - if (this.fileUploadSubscription) { - this.fileUploadSubscription.unsubscribe(); - } - - this.status = UploadStatus.error; - if (errorObj.status === 401) { - this.requestResponseText = 'Fehler: Zugriff verweigert - bitte (neu) anmelden!'; - } else if (errorObj.status === 503) { - this.requestResponseText = 'Fehler: Server meldet Problem mit Datenbank oder Datei zu groß.'; - } else if (errorObj.error instanceof ErrorEvent) { - this.requestResponseText = 'Fehler: ' + (<ErrorEvent>errorObj.error).message; - } else { - this.requestResponseText = 'Fehler: ' + errorObj.message; - } - }); - } - } - - // ================================================================== - public remove(): void { - if (this.fileUploadSubscription) { - this.fileUploadSubscription.unsubscribe(); - } - this.removeFileRequestEvent.emit(this); - } - -} - -export enum UploadStatus { - ready, - busy, - ok, - error -} diff --git a/src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.spec.ts b/src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.ts b/src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.ts deleted file mode 100644 index 994448e7..00000000 --- a/src/app/iqb-files/iqbFileUploadInputFor/iqbFileUploadInputFor.directive.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Component, Directive, ElementRef, EventEmitter, HostListener, - Input, OnDestroy, OnInit, Output } from '@angular/core'; - - - @Directive({ - selector: 'input[iqbFileUploadInputFor], div[iqbFileUploadInputFor]', - }) - export class IqbFileUploadInputForDirective { - - - private _queue: any = null; - private _element: HTMLElement; - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - constructor(private element: ElementRef) { - this._element = this.element.nativeElement; - } - - - @Input('iqbFileUploadInputFor') - set fileUploadQueue(value: any) { - if (value) { - this._queue = value; - } - } - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @HostListener('change') - public onChange(): any { - const files = this.element.nativeElement.files; - // this.onFileSelected.emit(files); - - for (let i = 0; i < files.length; i++) { - this._queue.add(files[i]); - } - this.element.nativeElement.value = ''; - } - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @HostListener('drop', [ '$event' ]) - public onDrop(event: any): any { - const files = event.dataTransfer.files; - // this.onFileSelected.emit(files); - - for (let i = 0; i < files.length; i++) { - this._queue.add(files[i]); - } - event.preventDefault(); - event.stopPropagation(); - this.element.nativeElement.value = ''; - } - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @HostListener('dragover', [ '$event' ]) - public onDropOver(event: any): any { - event.preventDefault(); - } - - } diff --git a/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.html b/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.html deleted file mode 100644 index 52ffbc49..00000000 --- a/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.html +++ /dev/null @@ -1,18 +0,0 @@ -<iqb-file-upload - [file]="file" - [id]="i" - *ngFor="let file of files; let i = index" - [httpUrl]="httpUrl" - [fileAlias]="fileAlias" - [tokenName]="tokenName" - [token]="token" - [folderName]="folderName" - [folder]="folder" - - (removeFileRequestEvent)="removeFile($event)" - (statusChangedEvent)="analyseStatus()"> -</iqb-file-upload> -<br/> -<button mat-raised-button color="primary" [disabled]="disableUploadAllButton" - *ngIf="files.length > 0" (click)="uploadAll()">Alle hochladen</button> -<button mat-raised-button color="primary" *ngIf="files.length > 0" (click)="removeAll()">Liste leeren</button> diff --git a/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.spec.ts b/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.ts b/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.ts deleted file mode 100644 index ca22751a..00000000 --- a/src/app/iqb-files/iqbFileUploadQueue/iqbFileUploadQueue.component.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Component, EventEmitter, OnInit, OnDestroy, QueryList, ViewChildren, Input, Output } from '@angular/core'; -import { IqbFileUploadComponent, UploadStatus } from './../iqbFileUpload/iqbFileUpload.component'; -import { Observable , merge } from 'rxjs'; -import { startWith } from 'rxjs/operators'; -import { HttpHeaders, HttpParams } from '@angular/common/http'; - - -/** - * A material design file upload queue component. - */ -@Component({ - selector: 'iqb-file-upload-queue', - templateUrl: `iqbFileUploadQueue.component.html`, - exportAs: 'iqbFileUploadQueue', - }) - export class IqbFileUploadQueueComponent implements OnDestroy { - - @ViewChildren(IqbFileUploadComponent) fileUploads: QueryList<IqbFileUploadComponent>; - - private files: Array<any> = []; - private numberOfErrors = 0; - private numberOfUploads = 0; - private disableUploadAllButton: boolean; - - /* Http request input bindings */ - @Input() - httpUrl: string; - - @Input() - httpRequestHeaders: HttpHeaders | { - [header: string]: string | string[]; - } = new HttpHeaders().set('Content-Type', 'multipart/form-data'); - - @Input() - httpRequestParams: HttpParams | { - [param: string]: string | string[]; - } = new HttpParams(); - - @Input() - fileAlias: string; - - @Input() - tokenName: string; - - @Input() - token: string; - - @Input() - folderName: string; - - @Input() - folder: string; - - @Output() uploadCompleteEvent = new EventEmitter<IqbFileUploadQueueComponent>(); - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - add(file: any) { - this.files.push(file); - this.disableUploadAllButton = false; - } - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - public uploadAll() { - this.fileUploads.forEach((fileUpload) => { - fileUpload.upload(); - }); - } - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - public removeAll() { - this.files.splice(0, this.files.length); - } - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - ngOnDestroy() { - if (this.files) { - this.removeAll(); - } - } - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - removeFile(fileToRemove: IqbFileUploadComponent) { - this.files.splice(fileToRemove.id, 1); - } - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - updateStatus() { - this.numberOfErrors = 0; - this.numberOfUploads = 0; - - this.fileUploads.forEach((fileUpload) => { - - fileUpload.upload(); - }); - } - - // +++++++++++++++++++++++++++++++++++++++++++++++++ - analyseStatus() { - let someoneiscomplete = false; - let someoneisbusy = false; - let someoneisready = false; - this.fileUploads.forEach((fileUpload) => { - if (fileUpload.status === UploadStatus.ok) { - someoneiscomplete = true; - } else if (fileUpload.status === UploadStatus.busy) { - someoneisbusy = true; - return; // forEach - } else if (fileUpload.status === UploadStatus.ready) { - someoneisready = true; - } - }); - - if (someoneiscomplete && !someoneisbusy) { - this.uploadCompleteEvent.emit(); - } - this.disableUploadAllButton = !someoneisready || someoneisbusy; - } -} diff --git a/src/app/logindata.service.spec.ts b/src/app/logindata.service.spec.ts new file mode 100644 index 00000000..4bc1cf2b --- /dev/null +++ b/src/app/logindata.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { LogindataService } from './logindata.service'; + +describe('LogindataService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [LogindataService] + }); + }); + + it('should be created', inject([LogindataService], (service: LogindataService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/logindata.service.ts b/src/app/logindata.service.ts new file mode 100644 index 00000000..8ceef376 --- /dev/null +++ b/src/app/logindata.service.ts @@ -0,0 +1,82 @@ +import { BackendService, ServerError, LoginData, BookletStatus, BookletDataList } from './backend.service'; +import { BehaviorSubject } from 'rxjs'; +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class LogindataService { + + public pageTitle$ = new BehaviorSubject<string>('IQB-Testcenter - Willkommen'); + public loginName$ = new BehaviorSubject<string>(''); + public loginMode$ = new BehaviorSubject<string>(''); + public workspaceName$ = new BehaviorSubject<string>(''); + public groupName$ = new BehaviorSubject<string>(''); + public personToken$ = new BehaviorSubject<string>(''); + public personCode$ = new BehaviorSubject<string>(''); + public bookletDbId$ = new BehaviorSubject<number>(0); + public bookletLabel$ = new BehaviorSubject<string>(''); + public globalErrorMsg$ = new BehaviorSubject<string>(''); + public allBooklets$ = new BehaviorSubject<BookletDataList>(null); + + constructor( + private bs: BackendService + ) { + // on reload of application: + // look for personToken and get booklets and (if stored) selected booklet + const pt = localStorage.getItem('pt'); + if ((typeof pt !== 'string') || (pt.length === 0)) { + localStorage.setItem('bi', ''); + console.log('yop'); + } else { + this.bs.getLoginDataByPersonToken(pt).subscribe(loginDataUntyped => { + if (loginDataUntyped instanceof ServerError) { + const e = loginDataUntyped as ServerError; + this.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + localStorage.setItem('pt', ''); + localStorage.setItem('bi', ''); + } else { + const loginData = loginDataUntyped as LoginData; + this.globalErrorMsg$.next(''); + this.groupName$.next(loginData.groupname); + this.workspaceName$.next(loginData.workspaceName); + this.personCode$.next(loginData.code); + this.allBooklets$.next(loginData.booklets); + this.loginMode$.next(loginData.mode); + this.personToken$.next(pt); + this.loginName$.next(loginData.loginname); + + const b = localStorage.getItem('bi'); + if (b !== null) { + this.bs.getBookletStatusByDbId(pt, +b).subscribe(bookletStatusUntyped => { + if (bookletStatusUntyped instanceof ServerError) { + const e = bookletStatusUntyped as ServerError; + this.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + localStorage.setItem('bi', ''); + } else { + const bookletStatus = bookletStatusUntyped as BookletStatus; + this.globalErrorMsg$.next(''); + if (bookletStatus.canStart) { + this.bookletDbId$.next(bookletStatus.id); + this.bookletLabel$.next(bookletStatus.label); + } else { + localStorage.setItem('bi', ''); + } + } + }); + } + } + } + ); + + } + + this.personToken$.subscribe(t => localStorage.setItem('pt', t)); + this.bookletDbId$.subscribe(id => localStorage.setItem('bi', id.toString())); + } + + // ***************************************************** + login(name: string, pw: string) { + + } +} diff --git a/src/app/shared/backend.service.ts b/src/app/shared/backend.service.ts deleted file mode 100644 index 107e7484..00000000 --- a/src/app/shared/backend.service.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { Injectable, Inject } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpEvent, HttpErrorResponse } from '@angular/common/http'; -import { Observable, throwError } from 'rxjs'; -import { catchError } from 'rxjs/operators'; - - - - - -@Injectable() -export class BackendService { - - constructor( - @Inject('SERVER_URL') private serverUrl: string, - private http: HttpClient) { } - - /* - getStatus(admintoken: string, logintoken: string, sessiontoken: string): Observable<LoginResponseData> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<LoginResponseData>(this.serverUrl + 'getStatus.php', {at: admintoken, lt: logintoken, st: sessiontoken}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } */ - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - testlogin(name: string, password: string): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<string>(this.serverUrl + 'testlogin.php', {n: name, p: password}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - getSessions(token: string): Observable<GetBookletsResponseData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<GetBookletsResponseData>(this.serverUrl + 'getBooklets.php', {lt: token}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - - getBookletStatus(logintoken: string, code: string, bookletId: string): Observable<GetBookletResponseData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<GetBookletResponseData>(this.serverUrl + 'getBookletStatus.php', { - lt: logintoken, c: code, b: bookletId}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - startSession(token: string, code: string, bookletFilename: string): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<string>(this.serverUrl + 'startSession.php', {lt: token, c: code, b: bookletFilename}, httpOptions) - .pipe( - catchError(this.handleError) - ); -} - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - private handleError(errorObj: HttpErrorResponse): Observable<ServerError> { - const myreturn: ServerError = { - label: 'Fehler bei Datenübertragung', - code: errorObj.status - }; - 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 Observable.throw(myreturn.label); - } -} - - -// ############################################################################################# - -export interface ServerError { - code: number; - label: string; -} - -export interface LoginResponseData { - t: string; - n: string; - ws: string; -} - -export interface BookletData { - name: string; - filename: string; - title: string; -} - -export interface BookletDataList { - [code: string]: BookletData[]; -} - -export interface GetBookletsResponseData { - mode: string; - ws: string; - booklets: {[code: string]: BookletData[]}; -} - -export interface GetBookletResponseData { - statusLabel: string; - lastUnit: number; - canStart: boolean; -} - diff --git a/src/app/shared/global-store.service.spec.ts b/src/app/shared/global-store.service.spec.ts deleted file mode 100644 index 9bd3b1ac..00000000 --- a/src/app/shared/global-store.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { GlobalStoreService } from './global-store.service'; - -describe('GlobalStoreService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [GlobalStoreService] - }); - }); - - it('should be created', inject([GlobalStoreService], (service: GlobalStoreService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/src/app/shared/global-store.service.ts b/src/app/shared/global-store.service.ts deleted file mode 100644 index 36bfee38..00000000 --- a/src/app/shared/global-store.service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BehaviorSubject } from 'rxjs'; -import { Injectable, Component, Input, Output, EventEmitter } from '@angular/core'; - -@Injectable() -export class GlobalStoreService { - - private _loginToken = ''; - - // title __________________________________________________ - public pageTitle$ = new BehaviorSubject('IQB-Testcenter - Willkommen'); - updatePageTitle(newTitle) { - this.pageTitle$.next(newTitle); - } - - // tokens __________________________________________________ - set loginToken(newToken: string) { - if (newToken !== this._loginToken) { - localStorage.setItem('lt', newToken); - this._loginToken = newToken; - } - } - get loginToken(): string { - if (this._loginToken.length === 0) { - this._loginToken = localStorage.getItem('lt'); - } - return this._loginToken; - } -} diff --git a/src/app/start/start.component.css b/src/app/start/start.component.css index cdba6bcb..16e70417 100644 --- a/src/app/start/start.component.css +++ b/src/app/start/start.component.css @@ -1,8 +1,8 @@ -div.codeinput, div.testselect { +div.inputform { margin: 40px; } -div.buttongroup { +div.bookletbuttongroup { display: flex; flex-direction: row; flex-wrap: wrap; @@ -18,7 +18,7 @@ form { width: 100%; } -button.testselectbutton { +button.bookletselectbutton { margin: 10px; } diff --git a/src/app/start/start.component.html b/src/app/start/start.component.html index a88ea679..bf63ef05 100644 --- a/src/app/start/start.component.html +++ b/src/app/start/start.component.html @@ -1,31 +1,98 @@ <div class="page-body"> - <div class="sheetofpaper"> - <div class="codeinput" *ngIf="showCodeinput"> - <p>Für das Starten eines Tests ist die Eingabe eines Start-Kennwortes nötig. Damit werden einzelne - Testpersonen erkennbar. Bei Fragen bitte an die Betreuerin bzw. den Betreuer des Tests wenden!</p> + <div class="sheetofpaper" fxLayout="row"> - <form [formGroup]="codeinputform" (ngSubmit)="codeinput()"> + <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> + <div fxFlex fxLayout="column" fxLayoutGap="30px"> + + <!-- - - - - - - - - - - - - - - - - --> + <div *ngIf="showLoginForm"> + <p>Das <a href="http://www.iqb.hu-berlin.de" target="_blank">Institut zur Qualitätsentwicklung im Bildungswesen</a> + betreibt auf diesen Seiten eine Pilotanwendung für das computerbasierte Leistungstesten von + Schülerinnen und Schülern. Der Zugang zu einem Test ist nur möglich, wenn Sie von Testverantwortlichen + Zugangsdaten erhalten haben, die Sie bitte rechts eingeben. Es sind keine weiteren Seiten öffentlich verfügbar.</p> + + <p>Die mit diesem Testsystem erhobenen Daten sind vertraulich und enthalten grundsätzlich keinen direkten + Personenbezug. Es werden z. B. nie Namen gespeichert. Um Auskünfte zu einer bestimmten Studie + zu erhalten, wenden Sie sich bitte an das <a href="mailto:mechtel@iqb.hu-berlin.de?subject=Zum%20IQB-Testsystem"> + IQB</a>. Wir benötigen dazu den ungefähren Testzeitraum und das Bundesland, in dem die Studie + durchgeführt wurde.</p> + </div> + + <!-- - - - - - - - - - - - - - - - - --> + <div *ngIf="!showLoginForm"> + <p>Anmeldestatus: {{ loginStatusText }}</p> + </div> + + <!-- - - - - - - - - - - - - - - - - --> + <div *ngIf="showCodeForm"> + <p>Für das Starten eines Tests ist die Eingabe eines Personen-Codes nötig.</p> + <p>{{ codeInputPromt }}</p> + </div> + + <!-- - - - - - - - - - - - - - - - - --> + <div *ngIf="showBookletButtons"> + <p *ngIf="bookletlist.length === 0">Fehler: Kein Testmaterial für diese Anmeldung gefunden.</p> + <p *ngIf="bookletlist.length === 1">{{ bookletSelectPromptOne }}</p> + <p *ngIf="bookletlist.length > 1">{{ bookletSelectPromptMany }}</p> + </div> + + <!-- - - - - - - - - - - - - - - - - --> + <div *ngIf="showTestRunningButtons"> + <p>Es wird gerade ein Test ausgeführt. Bitte wählen Sie durch Klichen auf einen der beiden Schaltflächen + rechts, ob der Test fortgesetzt oder beendet werden soll!</p> + </div> + </div> + + <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> + <div fxFlex="200px" fxLayout="column"> + <!-- - - - - - - - - - - - - - - - - --> + <form [formGroup]="testtakerloginform" (ngSubmit)="testtakerlogin()" *ngIf="showLoginForm"> + <mat-form-field class="full-width"> + <input matInput formControlName="testname" placeholder="Anmeldename"> + </mat-form-field> <mat-form-field class="full-width"> - <input matInput formControlName="code" placeholder="Kennwort"> + <input matInput type="password" formControlName="testpw" placeholder="Kennwort" (keyup.enter)="testtakerlogin()"> </mat-form-field> - <button mat-raised-button type="submit" [disabled]="codeinputform.invalid">Weiter</button> + <button mat-raised-button type="submit" [disabled]="testtakerloginform.invalid" color="primary">Weiter</button> </form> - <p *ngIf="isError">{{ errorMessage }}</p> - </div> - - <div class="testselect" *ngIf="showTeststartButtons"> - <p>{{ select_message }}</p> + <!-- - - - - - - - - - - - - - - - - --> + <form [formGroup]="codeinputform" (ngSubmit)="codeinput()" *ngIf="showCodeForm"> + <mat-form-field class="full-width"> + <input matInput formControlName="code" placeholder="Personen-Code"(keyup.enter)="codeinput()"> + </mat-form-field> + <button mat-raised-button type="submit" [disabled]="codeinputform.invalid" color="primary">Weiter</button> + </form> - <div class="buttongroup"> - <button mat-raised-button class="testselectbutton" (click)="buttonStartTest($event)" - [value]="t.filename_and_lastUnit" [disabled]="!t.isEnabled" *ngFor="let t of testlist"> - <p class="booklet_title">{{t.title}}</p> - <p class="booklet_status">{{t.statustxt}}</p> + <!-- - - - - - - - - - - - - - - - - --> + <div fxLayout="row" fxLayoutGap="20px" *ngIf="showBookletButtons"> + <p *ngIf="bookletlist.length === 0"> + Für diese Anmeldung wurde kein Test gefunden. + </p> + <button mat-raised-button class="bookletselectbutton" (click)="buttonStartTest($event)" + [value]="b.filename_and_lastUnit" [disabled]="!t.isEnabled" *ngFor="let b of bookletlist"> + <p class="booklet_title">{{b.title}}</p> + <p class="booklet_status">{{b.statustxt}}</p> </button> </div> + + <!-- - - - - - - - - - - - - - - - - --> + <div *ngIf="showTestRunningButtons"> + <div fxLayout="row" fxLayoutGap="20px"> + <button mat-raised-button color="primary">Zum Test zurückkehren</button> + <button mat-raised-button color="primary">Test beenden</button> + </div> + </div> + </div> - <p *ngIf="isError">{{ errorMessage }}</p> + <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> + <div fxFlex="200px" fxLayout="column"> + <button mat-raised-button color="foreground" *ngIf="!showLoginForm">Test-Anmeldung ändern</button> + <button mat-raised-button color="foreground" *ngIf="validCodes.length > 1">Personen-Code ändern</button> + <p *ngIf="!showLoginForm || validCodes.length > 1"> </p> + <button mat-raised-button color="foreground">Impressum/Datenschutz</button> + <p> {{ errorMsg }}</p> + </div> </div> </div> diff --git a/src/app/start/start.component.ts b/src/app/start/start.component.ts index ada50141..90345019 100644 --- a/src/app/start/start.component.ts +++ b/src/app/start/start.component.ts @@ -1,9 +1,10 @@ -import { TestdataService } from './../test-controller'; +import { map } from 'rxjs/operators'; +import { LogindataService } from './../logindata.service'; import { MessageDialogComponent, MessageDialogData, MessageType } from './../iqb-common'; import { MatDialog } from '@angular/material'; -import { BackendService, BookletData, GetBookletsResponseData, GetBookletResponseData } from './../shared/backend.service'; +import { BackendService, BookletData, PersonTokenAndBookletId, + BookletDataList, LoginData, BookletStatus, ServerError } from './../backend.service'; import { Router } from '@angular/router'; -import { GlobalStoreService } from './../shared/global-store.service'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { FormGroup, FormBuilder, FormArray, FormControl, Validators, ReactiveFormsModule } from '@angular/forms'; @@ -12,111 +13,200 @@ import { FormGroup, FormBuilder, FormArray, FormControl, Validators, ReactiveFor styleUrls: ['./start.component.css'] }) export class StartComponent implements OnInit { + // for template + private showLoginForm = true; + private loginStatusText = 'nicht angemeldet'; + private showCodeForm = false; + private codeInputPromt = 'Bitte gib den Personen-Code ein, den du auf dem Zettel am Platz gefunden hast!'; + private showBookletButtons = false; + private bookletlist: StartButtonData[] = []; + private bookletSelectPromptOne = 'Bitte klick auf die Schaltfläche rechts, um den Test zu starten!'; + private bookletSelectPromptMany = 'Bitte klicken Sie auf eine der Schaltflächen rechts, um einen Test zu starten!'; + private showTestRunningButtons = false; + private validCodes = []; - codeinputform: FormGroup; - public showCodeinput: boolean; - public showTeststartButtons: boolean; - public select_message: string; - private sessiondata: GetBookletsResponseData; - private testlist: StartButtonData[]; - private code = ''; - private isError = false; - private errorMessage = ''; + private testtakerloginform: FormGroup; + private codeinputform: FormGroup; + private errorMsg = ''; + private loginToken = ''; + + // ?? + // private sessiondata: PersonBooklets; + // private code = ''; + // private isError = false; + // private errorMessage = ''; constructor(private fb: FormBuilder, - private gss: GlobalStoreService, - private tss: TestdataService, + private lds: LogindataService, public messsageDialog: MatDialog, private router: Router, private bs: BackendService) { - this.showCodeinput = false; - this.showTeststartButtons = true; - this.select_message = 'Bitte warten.'; - this.testlist = []; - } + + } ngOnInit() { - this.gss.updatePageTitle('IQB-Testcenter - Start'); + this.lds.pageTitle$.next('IQB-Testcenter - Start'); + + this.lds.personToken$.subscribe(pt => { + const bId = this.lds.bookletDbId$.getValue(); + if (pt.length > 0) { + this.showLoginForm = false; + this.showCodeForm = false; + this.showBookletButtons = bId === 0; + this.showTestRunningButtons = bId > 0; + this.loginStatusText = 'angemeldet als "' + this.lds.loginName$.getValue(); + const code = this.lds.personCode$.getValue(); + if (code.length > 0) { + this.loginStatusText += '/' + code; + } + this.loginStatusText += '", ' + this.lds.loginMode$.getValue(); + } else { + this.showLoginForm = this.loginToken.length === 0; + this.showCodeForm = this.validCodes.length > 1; + this.showBookletButtons = false; + this.showTestRunningButtons = false; + this.loginStatusText = 'nicht angemeldet'; + } + }); + + this.lds.bookletDbId$.subscribe(id => { + const ptLength = this.lds.personToken$.getValue().length; + if (id > 0) { + this.showLoginForm = false; + this.showCodeForm = false; + this.showBookletButtons = false; + this.showTestRunningButtons = ptLength > 0; + } else { + this.showLoginForm = ptLength === 0; + this.showCodeForm = false; + this.showBookletButtons = ptLength > 0; + this.showTestRunningButtons = false; + } + }); + + 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)]) + }); this.codeinputform = this.fb.group({ code: this.fb.control('', [Validators.required, Validators.minLength(1)]) }); + } - this.bs.getSessions(this.gss.loginToken).subscribe( - (bdata: GetBookletsResponseData) => { - this.sessiondata = bdata; - - if (Object.keys(this.sessiondata.booklets).length > 1) { - this.showCodeinput = true; - this.showTeststartButtons = false; + // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + testtakerlogin() { + this.bs.login(this.testtakerloginform.get('testname').value, this.testtakerloginform.get('testpw').value).subscribe( + loginTokenUntyped => { + if (loginTokenUntyped instanceof ServerError) { + const e = loginTokenUntyped as ServerError; + this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + // no change in other data } else { - this.setTestselectButtons(); - } - }, (errormsg: string) => { - this.isError = true; - this.select_message = errormsg; + this.validCodes = []; + this.bookletlist = []; + this.lds.personToken$.next(''); + this.lds.personCode$.next(''); + this.lds.globalErrorMsg$.next(''); + this.lds.workspaceName$.next(''); + this.lds.allBooklets$.next(null); + this.lds.bookletDbId$.next(0); + this.lds.bookletLabel$.next(''); + this.lds.loginMode$.next(''); + this.lds.loginName$.next(''); + + this.loginToken = loginTokenUntyped as string; + + // overwrite all data + this.bs.getLoginDataByLoginToken(this.loginToken).subscribe( + loginDataUntyped => { + if (loginDataUntyped instanceof ServerError) { + const e = loginDataUntyped as ServerError; + this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + this.loginToken = ''; + } else { + const loginData = loginDataUntyped as LoginData; + this.lds.personToken$.next(''); + this.lds.allBooklets$.next(loginData.booklets); + this.lds.groupName$.next(loginData.groupname); + this.lds.workspaceName$.next(loginData.workspaceName); + this.lds.loginMode$.next(loginData.mode); + this.lds.loginName$.next(loginData.loginname); + + this.validCodes = Object.keys(loginData.booklets); + this.showLoginForm = false; + + if (this.validCodes.length > 1) { + this.showCodeForm = true; + } else { + this.lds.personCode$.next((this.validCodes.length > 0) ? this.validCodes[0] : ''); + this.showCodeForm = false; + this.showBookletButtons = true; + this.bookletlist = this.getStartButtonData(this.lds.allBooklets$.getValue()); + } + } + }); + } } ); } // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # codeinput() { - this.isError = false; - this.errorMessage = ''; - if (this.setTestselectButtons()) { - this.showCodeinput = false; - this.showTeststartButtons = true; + const myCode = this.codeinputform.get('code').value as string; + if (myCode.length === 0) { + this.messsageDialog.open(MessageDialogComponent, { + width: '400px', + data: <MessageDialogData>{ + title: 'Eingabe Personen-Code', + content: 'Bitte geben Sie einen Personen-Code ein!.', + type: MessageType.error + } + }); + } else if (this.validCodes.indexOf(myCode) < 0) { + this.messsageDialog.open(MessageDialogComponent, { + width: '400px', + data: <MessageDialogData>{ + title: 'Eingabe Personen-Code', + content: 'Für diesen Personen-Code liegen keine Informationen vor.', + type: MessageType.error + } + }); + } else { + this.lds.personCode$.next(myCode); + this.lds.personToken$.next(''); + this.showCodeForm = false; + this.showBookletButtons = true; + this.bookletlist = this.getStartButtonData(this.lds.allBooklets$.getValue()); } } // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - setTestselectButtons(): boolean { - let codeIsValid = true; - this.testlist = []; - if (Object.keys(this.sessiondata.booklets).length > 1) { - this.code = this.codeinputform.get('code').value; - codeIsValid = Object.keys(this.sessiondata.booklets).includes(this.code); - } else { - this.code = Object.keys(this.sessiondata.booklets)[0]; - } + getStartButtonData(allBooklets: BookletDataList): StartButtonData[] { + const myreturn: StartButtonData[] = []; + const lt = this.loginToken; + const pt = this.lds.personToken$.getValue(); + const code = this.lds.personCode$.getValue(); - if (codeIsValid) { - const myBooklets = <BookletData[]>this.sessiondata.booklets[this.code]; + if (pt.length > 0 || lt.length > 0) { + const myBooklets = allBooklets[this.lds.personCode$.getValue()]; for (const booklet of myBooklets) { const myTest = new StartButtonData(booklet.name, booklet.title, booklet.filename); - this.testlist.push(myTest); - myTest.loadStatus(this.bs, this.gss, this.code); - } - - if (this.testlist.length === 1) { - this.select_message = 'Bitte klicken Sie auf den Schalter unten, um den Test zu starten!'; - } else { - this.select_message = 'Bitte klicken Sie auf einen der Schalter unten, um den Test auszuwählen und zu starten!'; - } - if (this.sessiondata.mode === 'trial') { - this.select_message += ' Achtung: Dieser Test wird im Modus "trial" durchgeführt, d. h. es gelten keine '; - this.select_message += 'Zeitbeschränkungen, aber die Navigation ist so beschränkt wie im normalen Test.'; - } else if (this.sessiondata.mode === 'review') { - this.select_message += ' Achtung: Dieser Test wird im Modus "review" durchgeführt, d. h. es gelten keine '; - this.select_message += 'Zeitbeschränkungen und die Navigation ist nicht beschränkt.'; - this.select_message += ' Nutzen Sie das Menü oben rechts, um Kommentare zu vergeben!'; - } - } else { - - this.messsageDialog.open(MessageDialogComponent, { - width: '400px', - data: <MessageDialogData>{ - title: 'Eingabe Kennwort für Test', - content: 'Für dieses Kennwort wurde kein Test gefunden.', - type: MessageType.error + if (pt.length > 0) { + myTest.getBookletStatusByPersonToken(this.bs, pt); + } else { + myTest.getBookletStatusByLoginToken(this.bs, lt, code); } - }); + myreturn.push(myTest); + } } - - return codeIsValid; + return myreturn; } + // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # buttonStartTest(event) { let myElement = event.target; do { @@ -126,19 +216,60 @@ export class StartComponent implements OnInit { } while (myElement.localName !== 'button'); const ButtonDataSplits = myElement.value.split('##'); + const lt = this.loginToken; + const pt = this.lds.personToken$.getValue(); + const code = this.lds.personCode$.getValue(); - this.isError = false; - this.errorMessage = ''; - this.bs.startSession(this.gss.loginToken, this.code, ButtonDataSplits[0]).subscribe( - (sessiontoken: string) => { - this.tss.updateSessionToken(sessiontoken); - this.tss.gotoFirstUnit(ButtonDataSplits[1]); - }, (errormsg: string) => { - this.tss.updateSessionToken(''); - this.isError = true; - this.errorMessage = errormsg; - } - ); + if (pt.length > 0 || lt.length > 0) { + if (pt.length > 0) { + this.bs.startBookletByPersonToken(pt, ButtonDataSplits[0]).subscribe( + bookletIdUntyped => { + if (bookletIdUntyped instanceof ServerError) { + const e = bookletIdUntyped as ServerError; + this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + } else { + const bookletId = bookletIdUntyped as number; + if (bookletId > 0) { + this.lds.bookletDbId$.next(bookletId); + console.log('jippi: ' + bookletId.toString()); + // ************************************************ + + // this.router.navigateByUrl('/u'); + + // ************************************************ + } else { + this.lds.globalErrorMsg$.next('ungültige Anmeldung'); + } + } + } + ); + } else { + this.bs.startBookletByLoginToken(lt, code, ButtonDataSplits[0]).subscribe( + startDataUntyped => { + if (startDataUntyped instanceof ServerError) { + const e = startDataUntyped as ServerError; + this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label); + } else { + const startData = startDataUntyped as PersonTokenAndBookletId; + if (startData.bookletId > 0) { + this.lds.bookletDbId$.next(startData.bookletId); + this.lds.personToken$.next(startData.personToken); + console.log('jippi: ' + startData.bookletId.toString()); + // ************************************************ + + // this.router.navigateByUrl('/u'); + + // ************************************************ + } else { + this.lds.globalErrorMsg$.next('ungültige Anmeldung'); + } + } + } + ); + } + } else { + this.lds.globalErrorMsg$.next('ungültige Anmeldung'); + } } } @@ -153,7 +284,11 @@ export class StartButtonData { statustxt: string; filename_and_lastUnit: string; - constructor(name: string, title: string, filename: string) { + constructor( + name: string, + title: string, + filename: string + ) { this.name = name; this.title = title; this.filename = filename; @@ -161,11 +296,31 @@ export class StartButtonData { this.statustxt = 'Bitte warten'; } - loadStatus(bs: BackendService, gss: GlobalStoreService, code: string) { - bs.getBookletStatus(gss.loginToken, code, this.name).subscribe((respData: GetBookletResponseData) => { - this.statustxt = respData.statusLabel; - this.isEnabled = respData.canStart; - this.filename_and_lastUnit = this.filename + '##' + respData.lastUnit; + public getBookletStatusByLoginToken(bs: BackendService, loginToken: string, code: string) { + bs.getBookletStatusByNameAndLoginToken(loginToken, code, this.name).subscribe(respDataUntyped => { + if (respDataUntyped instanceof ServerError) { + const e = respDataUntyped as ServerError; + this.statustxt = e.code.toString() + ': ' + e.label; + } else { + const respData = respDataUntyped as BookletStatus; + this.statustxt = respData.statusLabel; + this.isEnabled = respData.canStart; + this.filename_and_lastUnit = this.filename + '##' + respData.lastUnit; + } + }); + } + + public getBookletStatusByPersonToken(bs: BackendService, personToken: string) { + bs.getBookletStatusByNameAndPersonToken(personToken, this.name).subscribe(respDataUntyped => { + if (respDataUntyped instanceof ServerError) { + const e = respDataUntyped as ServerError; + this.statustxt = e.code.toString() + ': ' + e.label; + } else { + const respData = respDataUntyped as BookletStatus; + this.statustxt = respData.statusLabel; + this.isEnabled = respData.canStart; + this.filename_and_lastUnit = this.filename + '##' + respData.lastUnit; + } }); } } diff --git a/src/app/test-controller/backend.service.spec.ts b/src/app/test-controller/backend.service.spec.ts deleted file mode 100644 index c31039d7..00000000 --- a/src/app/test-controller/backend.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { BackendService } from './backend.service'; - -describe('BackendService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [BackendService] - }); - }); - - it('should be created', inject([BackendService], (service: BackendService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/src/app/test-controller/backend.service.ts b/src/app/test-controller/backend.service.ts deleted file mode 100644 index 29c024e8..00000000 --- a/src/app/test-controller/backend.service.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { Injectable, Inject } from '@angular/core'; -import { HttpClient, HttpParams, HttpHeaders, HttpEvent, HttpErrorResponse } from '@angular/common/http'; -import { ResponseContentType } from '@angular/http'; -import { BehaviorSubject , Observable, throwError } from 'rxjs'; -import { catchError, map, tap } from 'rxjs/operators'; - - -@Injectable() -export class BackendService { - - private unitCache: UnitData[] = []; - private lastBookletState = ''; - private lastUnitResponses = ''; - private lastUnitRestorePoint = ''; - - constructor( - @Inject('SERVER_URL') private serverUrl: string, - private http: HttpClient) { } - - // 888888888888888888888888888888888888888888888888888888888888888888 - getSessionData(sessiontoken: string): Observable<SessionData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<SessionData>(this.serverUrl + 'getSessionData.php', {st: sessiontoken}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - setBookletStatus(sessiontoken: string, 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); - return this.http - .post<string>(this.serverUrl + 'setBookletStatus.php', {st: sessiontoken, state: state}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - getUnit(sessiontoken: string, unitid: string): Observable<UnitData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - const myUnitdata = this.unitCache[unitid]; - if (typeof myUnitdata === 'undefined') { - return this.http - .post<UnitData>(this.serverUrl + 'getUnit.php', {st: sessiontoken, u: unitid}, httpOptions) - .pipe( - tap(r => this.unitCache[unitid] = r), - catchError(this.handleError) - ); - } else { - return new Observable (observer => { - observer.next(myUnitdata); - observer.complete(); - }); - } - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - getUnitResource(sessiontoken: string, resId: string): Observable<string | ServerError> { - const myHttpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }), - responseType: 'arraybuffer' as 'json' - }; - - return this.http - .post<ArrayBuffer>(this.serverUrl + 'getUnitResource.php', {st: sessiontoken, r: resId}, myHttpOptions) - .pipe( - map((r: ArrayBuffer) => { - let str64 = ''; - const alen = r.byteLength; - for (let i = 0; i < alen; i++) { - str64 += String.fromCharCode(r[i]); - } - return window.btoa(str64); - }), - catchError(this.handleError) - ); - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - getUnitResource64(sessiontoken: string, resId: string): Observable<string | ServerError> { - const myHttpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }), - responseType: 'text' as 'json' - }; - - return this.http - .post<string>(this.serverUrl + 'getUnitResource64.php', {st: sessiontoken, r: resId}, myHttpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - getUnitResourceTxt(sessiontoken: string, resId: string): Observable<string | ServerError> { - const myHttpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }), - responseType: 'text' as 'json' - }; - - return this.http - .post<string>(this.serverUrl + 'getUnitResourceTxt.php', {st: sessiontoken, r: resId}, myHttpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - // 888888888888888888888888888888888888888888888888888888888888888888 - setUnitResponses(sessiontoken: string, unit: string, unitdata: string): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - if ((sessiontoken + unit + JSON.stringify(unitdata)) === this.lastUnitResponses) { - return new Observable(null); - } else { - this.lastUnitResponses = sessiontoken + unit + JSON.stringify(unitdata); - // todo: store the response for evaluation - return this.http - .post<string>(this.serverUrl + 'setUnitResponses.php', {st: sessiontoken, u: unit, responses: unitdata}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - setUnitRestorePoint(sessiontoken: string, unit: string, unitdata: string): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - if ((sessiontoken + unit + JSON.stringify(unitdata)) === this.lastUnitRestorePoint) { - return new Observable(null); - } else { - this.lastUnitRestorePoint = sessiontoken + unit + JSON.stringify(unitdata); - const myUnitdata: UnitData = this.unitCache[unit]; - if (typeof myUnitdata !== 'undefined') { - myUnitdata.restorepoint = unitdata; - } - return this.http - .post<string>(this.serverUrl + 'setUnitRestorePoint.php', {st: sessiontoken, u: unit, restorepoint: unitdata}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - } - - // 888888888888888888888888888888888888888888888888888888888888888888 - setUnitLog(sessiontoken: string, unit: string, unitdata: {}): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<string>(this.serverUrl + 'setUnitLog.php', {st: sessiontoken, u: unit, log: unitdata}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - private handleError(errorObj: HttpErrorResponse): Observable<ServerError> { - const myreturn: ServerError = { - label: 'Fehler bei Datenübertragung', - code: errorObj.status - }; - 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 Observable.throw(myreturn.label); - } -} - -export interface SessionData { - xml: string; - locked: boolean; - u: number; -} - -export interface UnitData { - xml: string; - restorepoint: string; - status: {}; -} - -export interface ServerError { - code: number; - label: string; -} diff --git a/src/app/test-controller/index.ts b/src/app/test-controller/index.ts deleted file mode 100644 index 2193da2f..00000000 --- a/src/app/test-controller/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { TestControllerComponent } from './test-controller.component'; -export { TestControllerModule } from './test-controller.module'; -export { TestdataService } from './testdata.service'; - diff --git a/src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.spec.ts b/src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.ts b/src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.ts deleted file mode 100644 index 14a2a8ee..00000000 --- a/src/app/test-controller/resize-IFrameChild/resize-IFrameChild.directive.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, Directive, ElementRef, EventEmitter, HostListener, - Input, OnDestroy, OnInit, Output } from '@angular/core'; - - - @Directive({ - selector: 'div[iqbResizeIFrameChild]' - }) - export class ResizeIFrameChildDirective { - - - private _element: HTMLElement; - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - constructor(private element: ElementRef) { - this._element = this.element.nativeElement; - } - - - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @HostListener('window:resize') - public onResize(): any { - const iFrameList = this._element.getElementsByTagName('iframe'); - if (iFrameList.length > 0) { - let myIFrame: HTMLIFrameElement; - myIFrame = iFrameList[0]; - const divHeight = this._element.clientHeight; - myIFrame.setAttribute('height', String(divHeight)); - } - } - } diff --git a/src/app/test-controller/test-controller-routing.module.ts b/src/app/test-controller/test-controller-routing.module.ts deleted file mode 100644 index 8a3187c2..00000000 --- a/src/app/test-controller/test-controller-routing.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { UnitActivateGuard, UnitDeactivateGuard, UnitResolver } from './unithost/unit-routing'; -import { UnithostComponent } from './unithost/unithost.component'; -import { TestControllerComponent } from './test-controller.component'; -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; - - - -const routes: Routes = [ - { - path: 't', - component: TestControllerComponent, - children: [ -// {path: '', redirectTo: 'u/0', pathMatch: 'full'}, -// {path: 'u', redirectTo: 'u/0', pathMatch: 'full'}, - {path: 'u/:u', - component: UnithostComponent, - // canActivate: [UnitActivateGuard], - // canDeactivate: [UnitDeactivateGuard], - resolve: { - unit: UnitResolver - } - } - ] - }]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class TestControllerRoutingModule { } diff --git a/src/app/test-controller/test-controller.component.spec.ts b/src/app/test-controller/test-controller.component.spec.ts deleted file mode 100644 index 3e6a5082..00000000 --- a/src/app/test-controller/test-controller.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TestControllerComponent } from './test-controller.component'; - -describe('TestControllerComponent', () => { - let component: TestControllerComponent; - let fixture: ComponentFixture<TestControllerComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ TestControllerComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(TestControllerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/test-controller/test-controller.component.ts b/src/app/test-controller/test-controller.component.ts deleted file mode 100644 index 3b5ff62f..00000000 --- a/src/app/test-controller/test-controller.component.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - template: `<router-outlet></router-outlet>`, - styles: ['#testcontroller-main > ng-component:last-child { width: 100%;}'] -}) -export class TestControllerComponent { } diff --git a/src/app/test-controller/test-controller.module.ts b/src/app/test-controller/test-controller.module.ts deleted file mode 100644 index 48949f28..00000000 --- a/src/app/test-controller/test-controller.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { TestdataService } from './testdata.service'; -import { BackendService } from './backend.service'; -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { TestControllerRoutingModule } from './test-controller-routing.module'; -import { UnithostComponent } from './unithost/unithost.component'; -import { MatProgressSpinnerModule } from '@angular/material'; -import { TestControllerComponent } from './test-controller.component'; -import { ResizeIFrameChildDirective } from './resize-IFrameChild/resize-IFrameChild.directive'; -import { routingProviders } from './unithost/unit-routing'; - - -@NgModule({ - imports: [ - CommonModule, - TestControllerRoutingModule, - MatProgressSpinnerModule - ], - declarations: [ - UnithostComponent, - TestControllerComponent, - ResizeIFrameChildDirective - ], - providers: [ - TestdataService, - BackendService, - routingProviders - ], - exports: [ - TestControllerComponent - ] -}) -export class TestControllerModule { } 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 beaf8f65..00000000 --- a/src/app/test-controller/testdata.service.ts +++ /dev/null @@ -1,468 +0,0 @@ -import { BehaviorSubject , Observable } from 'rxjs'; -import { BackendService, SessionData, 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 pageTitle$ = new BehaviorSubject<string>('Lade Seite...'); - public navPrevEnabled$ = new BehaviorSubject<boolean>(false); - public navNextEnabled$ = new BehaviorSubject<boolean>(false); - 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); - - get sessionToken(): string { - return this._sessionToken; - } - - - // ................................................................................. - private _sessionToken = ''; - private allUnits: UnitDef[] = []; - private currentUnitId$ = new BehaviorSubject<number>(0); - - // 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.isSession$.subscribe(isSession => { - if (isSession) { - - } else { - this.updateBookletData('?', [], '', ''); - } - }); */ - } - - 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 }); - } - - // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ - // app.component.ngOnInit sets a listener on 'message'-event. - processMessagePost(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 { - -} - -// ..................................................................... -export class ResourceData { - filename: string; - id: string; - type: string; - dataString: string; - - constructor(filename: string, id: string) { - if ((typeof filename === 'undefined') || (filename == null)) { - filename = ''; - } - if ((typeof id === 'undefined') || (id == null)) { - id = ''; - } - - if ((filename + id).length === 0) { - this.filename = '?'; - this.id = '?'; - } else { - if (filename.length === 0) { - this.filename = id; - } else { - this.filename = filename; - } - if (id.length === 0) { - this.id = filename; - } else { - this.id = id; - } - } - } -} - -// ..................................................................... -export class UnitDef { - sequenceId: number; - name: string; - title: string; - resources: ResourceData[]; - restorePoint: string; - dataForItemplayer: string; - - constructor(name: string, title: string) { - this.name = name; - this.title = title; - this.resources = []; - this.restorePoint = ''; - this.dataForItemplayer = ''; - } - - getItemplayerHtml() { - for (let i = 0; i < this.resources.length; i++) { - if (this.resources[i].type === 'itemplayer_html') { - if (this.resources[i].dataString.length > 0) { - return this.resources[i].dataString; - } - } - } - return null; - } - - getResourcesAsDictionary(): {[resourceID: string]: string} { - const myResources = {}; - for (let i = 0; i < this.resources.length; i++) { - if (this.resources[i].type !== 'itemplayer_html') { - myResources[this.resources[i].id] = this.resources[i].dataString; - } - } - return myResources; - } -} diff --git a/src/app/test-controller/unithost/unit-routing.spec.ts b/src/app/test-controller/unithost/unit-routing.spec.ts deleted file mode 100644 index 2dbb0994..00000000 --- a/src/app/test-controller/unithost/unit-routing.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { TestBed, async, inject } from '@angular/core/testing'; - -import { UnitActivateGuard, UnitDeactivateGuard } from './unit-routing'; - -describe('UnitActivateGuard', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [UnitActivateGuard] - }); - }); - - it('should ...', inject([UnitActivateGuard], (guard: UnitActivateGuard) => { - expect(guard).toBeTruthy(); - })); -}); - - -describe('UnitDeactivateGuard', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [UnitDeactivateGuard] - }); - }); - - it('should ...', inject([UnitDeactivateGuard], (guard: UnitDeactivateGuard) => { - expect(guard).toBeTruthy(); - })); -}); diff --git a/src/app/test-controller/unithost/unit-routing.ts b/src/app/test-controller/unithost/unit-routing.ts deleted file mode 100644 index 6041850b..00000000 --- a/src/app/test-controller/unithost/unit-routing.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { switchMap, map } from 'rxjs/operators'; -import { ResourceData } from './../testdata.service'; -import { BackendService, ServerError } from './../backend.service'; -import { UnithostComponent } from './unithost.component'; -import { Injectable, Component } from '@angular/core'; -import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, Resolve } from '@angular/router'; -import { Observable } from 'rxjs'; - -import { UnitDef, TestdataService } from '../testdata.service'; - -@Injectable() -export class UnitActivateGuard implements CanActivate { - canActivate( - next: ActivatedRouteSnapshot, - state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { - console.log('UnitActivateGuard'); - return true; - } -} - -@Injectable() -export class UnitDeactivateGuard implements CanDeactivate<UnithostComponent> { - canDeactivate( - component: UnithostComponent, - next: ActivatedRouteSnapshot, - state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { - console.log('UnitDeactivateGuard'); - return true; - } -} - -@Injectable() -// enriches the routing data with unit data and resources: -// places in data['unit'] the unit object -export class UnitResolver implements Resolve<UnitDef> { - constructor(private tss: TestdataService, - private bs: BackendService) { } - - resolve(next: ActivatedRouteSnapshot, - state: RouterStateSnapshot): Observable<UnitDef> { - if (this.tss.isSession$.getValue() === true) { - return this.tss.getUnitAt(next.params['u']) - .pipe( - switchMap((newUnit: UnitDef) => { - this.tss.unitname$.next(newUnit.name); - return this.tss.fetchUnitData(newUnit); - })); - } else { - this.tss.unitname$.next(''); - return null; - } - } -} - - -export const routingProviders = [UnitActivateGuard, UnitDeactivateGuard, UnitResolver]; diff --git a/src/app/test-controller/unithost/unithost.component.css b/src/app/test-controller/unithost/unithost.component.css deleted file mode 100644 index 5aebe20f..00000000 --- a/src/app/test-controller/unithost/unithost.component.css +++ /dev/null @@ -1,3 +0,0 @@ -#iFrameHost { - width: 100%; -} diff --git a/src/app/test-controller/unithost/unithost.component.html b/src/app/test-controller/unithost/unithost.component.html deleted file mode 100644 index 6ea1e66e..00000000 --- a/src/app/test-controller/unithost/unithost.component.html +++ /dev/null @@ -1,3 +0,0 @@ -<!-- <p *ngIf="showMessage">{{ message }}</p>--> - <div id="iFrameHost" class="page-body" iqbResizeIFrameChild> - </div> diff --git a/src/app/test-controller/unithost/unithost.component.spec.ts b/src/app/test-controller/unithost/unithost.component.spec.ts deleted file mode 100644 index 7c5cf61a..00000000 --- a/src/app/test-controller/unithost/unithost.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { UnithostComponent } from './unithost.component'; - -describe('UnithostComponent', () => { - let component: UnithostComponent; - let fixture: ComponentFixture<UnithostComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ UnithostComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UnithostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/test-controller/unithost/unithost.component.ts b/src/app/test-controller/unithost/unithost.component.ts deleted file mode 100644 index c53a0eca..00000000 --- a/src/app/test-controller/unithost/unithost.component.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Subscriber , Subscription } from 'rxjs'; -import { BackendService } from './../backend.service'; -import { TestdataService, UnitDef, ResourceData } from './../testdata.service'; -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { OnDestroy } from '@angular/core/src/metadata/lifecycle_hooks'; -import { Location } from '@angular/common'; - -@Component({ - templateUrl: './unithost.component.html', - styleUrls: ['./unithost.component.css'] -}) - -export class UnithostComponent implements OnInit, OnDestroy { - private message = ''; - - // public showIframe = false; - private iFrameHostElement: HTMLElement; - private iFrameItemplayer: HTMLIFrameElement; - private routingSubscription: Subscription; - - constructor( - private tss: TestdataService, - private bs: BackendService, - private location: Location, - private route: ActivatedRoute - ) { - this.tss.statusmessage$.subscribe(s => { - this.message = s; - }); - } - - ngOnInit() { - this.iFrameHostElement = <HTMLElement>document.querySelector('#iFrameHost'); - - this.iFrameItemplayer = null; - - this.routingSubscription = this.route.params.subscribe( - params => { - // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV - while (this.iFrameHostElement.hasChildNodes()) { - this.iFrameHostElement.removeChild(this.iFrameHostElement.lastChild); - } - const newUnit: UnitDef = this.route.snapshot.data['unit']; - - if ((newUnit === null) || (newUnit === undefined)) { - const messageElement = <HTMLElement>document.createElement('p'); - messageElement.setAttribute('class', 'unitMessage'); - messageElement.innerHTML = this.tss.statusmessage$.getValue(); - this.iFrameHostElement.appendChild(messageElement); - - this.tss.updatePageTitle('Problem?'); - this.tss.updateUnitId(-1); - } else { - this.tss.updatePageTitle(newUnit.title); - this.tss.updateUnitId(newUnit.sequenceId); - - this.iFrameItemplayer = <HTMLIFrameElement>document.createElement('iframe'); - this.iFrameItemplayer.setAttribute('srcdoc', newUnit.getItemplayerHtml()); - this.iFrameItemplayer.setAttribute('sandbox', 'allow-forms allow-scripts allow-same-origin'); - this.iFrameItemplayer.setAttribute('class', 'unitHost'); - this.iFrameItemplayer.setAttribute('height', String(this.iFrameHostElement.clientHeight)); - - this.tss.pendingItemDefinition$.next(newUnit.dataForItemplayer); - this.tss.pendingItemResources$.next(newUnit.getResourcesAsDictionary()); - this.tss.pendingItemRestorePoint$.next(newUnit.restorePoint); - - this.iFrameHostElement.appendChild(this.iFrameItemplayer); - } - }); - } - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - ngOnDestroy() { - this.routingSubscription.unsubscribe(); - } -} diff --git a/src/environments/environment.ts b/src/environments/environment.ts index d2df3ae3..b5d68c88 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,7 +4,7 @@ export const environment = { production: false, - testcenterUrl: 'https://ocba2.iqb.hu-berlin.de/', + testcenterUrl: 'https://mdr2.iqb.hu-berlin.de/', appName: 'IQB-Testcenter', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', appVersion: '0 (dev)' -- GitLab