diff --git a/package.json b/package.json index 456223dee08034cbe119228cc247eb349527eb0e..55c168b5fa11cec254601c2d582f0fa7ff9d7f1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "itc-ng", - "version": "1.5.2", + "version": "1.5.3", "scripts": { "ng": "ng", "start": "ng serve", diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..56b6f3523cfc793edd4a8aff11e75ce45ae87c1a --- /dev/null +++ b/src/app/admin/admin-routing.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import {WorkspaceComponent} from "./workspace.component"; +import {DummyComponent} from "./dummy/dummy.component"; + + +const routes: Routes = [ + { + path: ':ws', + component: WorkspaceComponent, + children: [ + {path: '', redirectTo: 'monitor', pathMatch: 'full'}, + {path: 'files', component: DummyComponent}, + {path: 'syscheck', component: DummyComponent}, + {path: 'monitor', component: DummyComponent}, + {path: 'results', component: DummyComponent}, + {path: '**', component: DummyComponent} + ] + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AdminRoutingModule { } diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa206843d2d185891d5a6baa123323a96572e379 --- /dev/null +++ b/src/app/admin/admin.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AdminRoutingModule } from './admin-routing.module'; +import { WorkspaceComponent } from './workspace.component'; +import { DummyComponent } from './dummy/dummy.component'; + + +@NgModule({ + declarations: [WorkspaceComponent, DummyComponent], + imports: [ + CommonModule, + AdminRoutingModule + ], + exports: [ + WorkspaceComponent, + DummyComponent + ] +}) +export class AdminModule { } diff --git a/src/app/admin/dummy/dummy.component.css b/src/app/admin/dummy/dummy.component.css new file mode 100644 index 0000000000000000000000000000000000000000..fbf04dc85472b7854e6dee1985f6dc571362bef9 --- /dev/null +++ b/src/app/admin/dummy/dummy.component.css @@ -0,0 +1,4 @@ +p { + margin: 10px; + color: yellow; +} diff --git a/src/app/admin/dummy/dummy.component.html b/src/app/admin/dummy/dummy.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0c34d90802a2d866a91462b5c9e54ad238600aeb --- /dev/null +++ b/src/app/admin/dummy/dummy.component.html @@ -0,0 +1 @@ +<p>dummy works!</p> diff --git a/src/app/admin/dummy/dummy.component.spec.ts b/src/app/admin/dummy/dummy.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..52111f871a88d73ccfbd11fdea147f3c92f26c4d --- /dev/null +++ b/src/app/admin/dummy/dummy.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DummyComponent } from './dummy.component'; + +describe('DummyComponent', () => { + let component: DummyComponent; + let fixture: ComponentFixture<DummyComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DummyComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DummyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/dummy/dummy.component.ts b/src/app/admin/dummy/dummy.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..226b14bc7378dac188f17b15b047d24653e7d098 --- /dev/null +++ b/src/app/admin/dummy/dummy.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-dummy', + templateUrl: './dummy.component.html', + styleUrls: ['./dummy.component.css'] +}) +export class DummyComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/admin/workspace.component.css b/src/app/admin/workspace.component.css new file mode 100644 index 0000000000000000000000000000000000000000..6da29efa857cb699c343691414dd2ccda9286bfb --- /dev/null +++ b/src/app/admin/workspace.component.css @@ -0,0 +1,4 @@ +p { + margin: 10px; + color: aliceblue; +} diff --git a/src/app/admin/workspace.component.html b/src/app/admin/workspace.component.html new file mode 100644 index 0000000000000000000000000000000000000000..806da3409b9da517e7b4d74540f2e0ce4006a307 --- /dev/null +++ b/src/app/admin/workspace.component.html @@ -0,0 +1,8 @@ +<p>WorkspaceComponent</p> +<p>Eingeloggt als {{ (mds.loginData$ | async)?.loginname}}</p> +<p>Workspace: {{myWorkspace}}</p> +<p> </p> +<p>Sorry - die Einbindung der Administrator-Funktionen in das Testcenter ist noch in Arbeit.</p> + +<hr/> +<router-outlet></router-outlet> diff --git a/src/app/admin/workspace.component.spec.ts b/src/app/admin/workspace.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ea31a8f29e3256b71a7d1772483fae203751971 --- /dev/null +++ b/src/app/admin/workspace.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WorkspaceComponent } from './workspace.component'; + +describe('WorkspaceComponent', () => { + let component: WorkspaceComponent; + let fixture: ComponentFixture<WorkspaceComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ WorkspaceComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WorkspaceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/workspace.component.ts b/src/app/admin/workspace.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f0cb6b3f30f22c89c0511b305411fd08ad3a146 --- /dev/null +++ b/src/app/admin/workspace.component.ts @@ -0,0 +1,30 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {MainDataService} from "../maindata.service"; +import {ActivatedRoute} from "@angular/router"; +import {Subscription} from "rxjs"; + +@Component({ + templateUrl: './workspace.component.html', + styleUrls: ['./workspace.component.css'] +}) +export class WorkspaceComponent implements OnInit, OnDestroy { + private routingSubscription: Subscription = null; + public myWorkspace = -1; + + constructor( + private route: ActivatedRoute, + public mds: MainDataService + ) { } + + ngOnInit() { + this.routingSubscription = this.route.params.subscribe(params => { + this.myWorkspace = Number(params['ws']); + }); + } + + ngOnDestroy() { + if (this.routingSubscription !== null) { + this.routingSubscription.unsubscribe(); + } + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 797af8d39201d19688467347ea6abd91e8e91b89..fc17a2f177864f38714c40fd98aa07d84a6c400b 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,4 +1,3 @@ -import { TestControllerComponent } from './test-controller'; import { AboutComponent } from './about/about.component'; import { StartComponent } from './start/start.component'; import { NgModule } from '@angular/core'; @@ -10,6 +9,8 @@ const routes: Routes = [ {path: 'start', component: StartComponent}, {path: 'about', component: AboutComponent}, {path: 'check', loadChildren: './sys-check/sys-check.module#SysCheckModule'}, + {path: 'admin', loadChildren: './admin/admin.module#AdminModule'}, + {path: 'superadmin', loadChildren: './superadmin/superadmin.module#SuperadminModule'}, {path: 't', loadChildren: './test-controller/test-controller.module#TestControllerModule'} ]; diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b68b0ea55cb20fc9f361d3755f65d551f0ea499f..cb7cd04fbd5cb01dc96994960b469d65466e7dde 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -20,10 +20,20 @@ export class AppComponent implements OnInit { private cts: CustomtextService ) { } + private static getStringFromLocalStorage(key: string) { + const storageEntry = localStorage.getItem(key); + if (storageEntry !== null) { + if (storageEntry.length > 0) { + return (storageEntry as string); + } + } + return '' + } + ngOnInit() { - this.mds.setCustomtextsFromDefList(appconfig.customtextsApp); - this.mds.setCustomtextsFromDefList(appconfig.customtextsLogin); - this.mds.setCustomtextsFromDefList(appconfig.customtextsBooklet); + this.mds.addCustomtextsFromDefList(appconfig.customtextsApp); + this.mds.addCustomtextsFromDefList(appconfig.customtextsLogin); + this.mds.addCustomtextsFromDefList(appconfig.customtextsBooklet); // give a message to the central message broadcast @@ -39,27 +49,31 @@ export class AppComponent implements OnInit { this.bs.getSysConfig().subscribe(sc => { this.mds.setDefaultCustomtexts(sc); - this.mds.setCustomtextsFromDefList(appconfig.customtextsApp); + this.mds.addCustomtextsFromDefList(appconfig.customtextsApp); // restore login status if stored in localStorage - const loginToken = localStorage.getItem('lt'); - if (loginToken !== null) { - if (loginToken.length > 0) { - let personToken = localStorage.getItem('pt'); - let bookletDbId = 0; - if (personToken !== null) { - if (personToken.length > 0) { - const bookletDbIdStr = localStorage.getItem('bi'); - if (bookletDbIdStr !== null) { - bookletDbId = Number(bookletDbIdStr); - } + const adminToken = AppComponent.getStringFromLocalStorage('at'); + if (adminToken) { + this.bs.getLoginDataAdmin(adminToken).subscribe( + (admindata: LoginData) => { + if (admindata instanceof ServerError) { + this.mds.setNewLoginData(); + } else { + this.mds.setNewLoginData(admindata); } - } else { - personToken = ''; } - let code = localStorage.getItem('c'); - if (code === null) { - code = ''; + ); + } else { + const loginToken = AppComponent.getStringFromLocalStorage('lt'); + if (loginToken) { + let personToken = AppComponent.getStringFromLocalStorage('pt'); + let bookletDbId = 0; + if (personToken) { + const bookletDbIdStr = AppComponent.getStringFromLocalStorage('bi'); + if (bookletDbIdStr) { + bookletDbId = Number(bookletDbIdStr); + } } + const code = AppComponent.getStringFromLocalStorage('c'); // bookletDbId is not yet checked by getLoginData, only passed-through this.bs.getLoginData(loginToken, personToken, bookletDbId).subscribe(ld => { @@ -81,13 +95,9 @@ export class AppComponent implements OnInit { }); } else { this.mds.setNewLoginData(); - this.mds.setCustomtextsFromDefList(appconfig.customtextsLogin); - this.mds.setCustomtextsFromDefList(appconfig.customtextsBooklet); + this.mds.addCustomtextsFromDefList(appconfig.customtextsLogin); + this.mds.addCustomtextsFromDefList(appconfig.customtextsBooklet); } - } else { - this.mds.setNewLoginData(); - this.mds.setCustomtextsFromDefList(appconfig.customtextsLogin); - this.mds.setCustomtextsFromDefList(appconfig.customtextsBooklet); } }); } diff --git a/src/app/app.interfaces.ts b/src/app/app.interfaces.ts index 2844088fb37aed179d878363dc7673b3eadb0108..e51bbcc375f69220ff9f68fb1244e09bfccdc5c5 100644 --- a/src/app/app.interfaces.ts +++ b/src/app/app.interfaces.ts @@ -15,6 +15,7 @@ export interface BookletListByCode { export interface LoginData { logintoken: string; loginname: string; + name: string; // TODO is loginname, but backend sends name if admin login mode: string; groupname: string; workspaceName: string; @@ -25,6 +26,9 @@ export interface LoginData { bookletlabel: string; customTexts: KeyValuePair; costumTexts: KeyValuePair; // TODO when backend fixed then change here + admintoken: string; + workspaces: WorkspaceData[]; + is_superadmin: boolean } export interface BookletStatus { @@ -47,3 +51,9 @@ export interface KeyValuePair { export interface KeyValuePairNumber { [K: string]: number; } + +export interface WorkspaceData { + id: number; + name: string; + role: string; +} diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts index e232513003be230468f01728ec9d1c28eb87ab9f..74f8455f190d40826ed10a3fad84631db3878329 100644 --- a/src/app/backend.service.ts +++ b/src/app/backend.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { catchError } from 'rxjs/operators'; +import {catchError, switchMap} from 'rxjs/operators'; import { LoginData, BookletStatus, PersonTokenAndBookletDbId, KeyValuePair } from './app.interfaces'; import {ErrorHandler, ServerError} from "iqb-components"; @@ -10,12 +10,14 @@ import {ErrorHandler, ServerError} from "iqb-components"; @Injectable() export class BackendService { private serverSlimUrl = ''; + private serverSlimAdminUrl = ''; private serverSlimUrl_Close = ''; constructor( @Inject('SERVER_URL') private readonly serverUrl: string, private http: HttpClient) { this.serverSlimUrl = this.serverUrl + 'php_tc/login.php/'; + this.serverSlimAdminUrl = this.serverUrl + 'admin/php/login.php/'; this.serverSlimUrl_Close = this.serverUrl + 'php_tc/tc_post.php/'; this.serverUrl = this.serverUrl + 'php_start/'; } @@ -24,10 +26,25 @@ export class BackendService { // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB login(name: string, password: string): Observable<LoginData | ServerError> { return this.http - .post<LoginData>(this.serverSlimUrl + 'login', {n: name, p: password}) + .post<LoginData>(this.serverSlimAdminUrl + 'login', {n: name, p: password}) .pipe( - catchError(ErrorHandler.handle) - ); + catchError(ErrorHandler.handle), + switchMap(myLoginData => { + if (myLoginData instanceof ServerError) { + if ((myLoginData as ServerError).code == 401) { + return this.http + .post<LoginData>(this.serverSlimUrl + 'login', {n: name, p: password}) + .pipe( + catchError(ErrorHandler.handle) + ); + } else { + return of(myLoginData) + } + } else { + return of(myLoginData); + } + }) + ) } // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB @@ -39,6 +56,14 @@ export class BackendService { ); } + getLoginDataAdmin(adminToken: string): Observable<LoginData | ServerError> { + return this.http + .post<LoginData>(this.serverSlimAdminUrl + 'login', {at: adminToken}) + .pipe( + catchError(ErrorHandler.handle) + ); + } + // BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB getSysConfig(): Observable<KeyValuePair> { return this.http.get<KeyValuePair>(this.serverSlimUrl + 'sysconfig') diff --git a/src/app/maindata.service.ts b/src/app/maindata.service.ts index 452e6b221a26a4f4c5419bcb04a8d1aa8a78b842..0d6600cc17b71fb6c539de4779df1e5b8dd7dd21 100644 --- a/src/app/maindata.service.ts +++ b/src/app/maindata.service.ts @@ -9,20 +9,26 @@ import { appconfig, customtextKeySeparator, CustomTextsDefList } from './app.con providedIn: 'root' }) export class MainDataService { - private static defaultLoginData: LoginData = { - logintoken: '', - persontoken: '', - mode: '', - groupname: '', - loginname: '', - workspaceName: '', - booklets: null, - code: '', - booklet: 0, - bookletlabel: '', - customTexts: {}, - costumTexts: {} - }; + private static get defaultLoginData(): LoginData { + return { + logintoken: '', + persontoken: '', + mode: '', + groupname: '', + loginname: '', + name: '', + workspaceName: '', + booklets: null, + code: '', + booklet: 0, + bookletlabel: '', + customTexts: {}, + admintoken: '', + workspaces: [], + is_superadmin: false, + costumTexts: {} + } + } public loginData$ = new BehaviorSubject<LoginData>(MainDataService.defaultLoginData); public globalErrorMsg$ = new BehaviorSubject<ServerError>(null); @@ -30,6 +36,15 @@ export class MainDataService { // set by app.component.ts public postMessage$ = new Subject<MessageEvent>(); + public get adminToken(): string { + const myLoginData = this.loginData$.getValue(); + if (myLoginData) { + return myLoginData.admintoken; + } else { + return ''; + } + } + constructor( private bs: BackendService, private cts: CustomtextService @@ -37,56 +52,54 @@ export class MainDataService { // ensures consistency setNewLoginData(logindata?: LoginData) { - const myLoginData: LoginData = { - logintoken: MainDataService.defaultLoginData.logintoken, - persontoken: MainDataService.defaultLoginData.persontoken, - mode: MainDataService.defaultLoginData.mode, - groupname: MainDataService.defaultLoginData.groupname, - loginname: MainDataService.defaultLoginData.loginname, - workspaceName: MainDataService.defaultLoginData.workspaceName, - booklets: MainDataService.defaultLoginData.booklets, - code: MainDataService.defaultLoginData.code, - booklet: MainDataService.defaultLoginData.booklet, - bookletlabel: MainDataService.defaultLoginData.bookletlabel, - customTexts: {}, // always ignored except right after getting from backend! - costumTexts: {} // always ignored except right after getting from backend! - }; - - if (logindata) { - if ( - (logindata.logintoken.length > 0) && - (logindata.loginname.length > 0) && - (logindata.mode.length > 0) && - (logindata.groupname.length > 0) && - (logindata.workspaceName.length > 0) && - (logindata.booklets)) { - - const validCodes = Object.keys(logindata.booklets); - if (validCodes.length > 0) { - myLoginData.logintoken = logindata.logintoken; - myLoginData.loginname = logindata.loginname; - myLoginData.mode = logindata.mode; - myLoginData.groupname = logindata.groupname; - myLoginData.workspaceName = logindata.workspaceName; - myLoginData.booklets = logindata.booklets; - if (logindata.code.length > 0) { - if (logindata.code in logindata.booklets) { - myLoginData.code = logindata.code; - } + const myLoginData: LoginData = MainDataService.defaultLoginData; + if (!logindata) { + logindata = MainDataService.defaultLoginData; + } + + if ((logindata.admintoken)) { //.length > 0) && (logindata.name.length > 0)) { + myLoginData.admintoken = logindata.admintoken; + if (logindata.name) { + myLoginData.loginname = logindata.name; + } else { + myLoginData.loginname = logindata.loginname; + } + myLoginData.workspaces = logindata.workspaces; + myLoginData.is_superadmin = logindata.is_superadmin; + } else if ( + (logindata.logintoken.length > 0) && + (logindata.loginname.length > 0) && + (logindata.mode.length > 0) && + (logindata.groupname.length > 0) && + (logindata.workspaceName.length > 0) && + (logindata.booklets)) { + + const validCodes = Object.keys(logindata.booklets); + if (validCodes.length > 0) { + myLoginData.logintoken = logindata.logintoken; + myLoginData.loginname = logindata.loginname; + myLoginData.mode = logindata.mode; + myLoginData.groupname = logindata.groupname; + myLoginData.workspaceName = logindata.workspaceName; + myLoginData.booklets = logindata.booklets; + if (logindata.code.length > 0) { + if (logindata.code in logindata.booklets) { + myLoginData.code = logindata.code; } - if (logindata.persontoken.length > 0) { - myLoginData.persontoken = logindata.persontoken; - myLoginData.booklet = logindata.booklet; - if (myLoginData.booklet > 0) { - myLoginData.bookletlabel = logindata.bookletlabel; - } + } + if (logindata.persontoken.length > 0) { + myLoginData.persontoken = logindata.persontoken; + myLoginData.booklet = logindata.booklet; + if (myLoginData.booklet > 0) { + myLoginData.bookletlabel = logindata.bookletlabel; } } - } - + } } + this.loginData$.next(myLoginData); localStorage.setItem('lt', myLoginData.logintoken); + localStorage.setItem('at', myLoginData.admintoken); localStorage.setItem('pt', myLoginData.persontoken); localStorage.setItem('bi', myLoginData.booklet.toString()); } @@ -144,7 +157,7 @@ export class MainDataService { return myLoginData.persontoken; } - public setCustomtextsFromDefList(customtextList: CustomTextsDefList) { + public addCustomtextsFromDefList(customtextList: CustomTextsDefList) { const myCustomTexts: {[key: string]: string} = {}; for (const ct of Object.keys(customtextList.defList)) { myCustomTexts[customtextList.keyPrefix + customtextKeySeparator + ct] = customtextList.defList[ct].defaultvalue; @@ -152,25 +165,27 @@ export class MainDataService { this.cts.addCustomTexts(myCustomTexts); } - public setDefaultCustomtexts(newTexts: {[key: string]: string; }) { - for (const ctKey of Object.keys(newTexts)) { - const sepIndex = ctKey.indexOf(customtextKeySeparator); - if (sepIndex > 1) { - const keyPrefix = ctKey.slice(0 , sepIndex - 1); - const keyId = ctKey.slice(sepIndex + 1); - - switch (keyPrefix) { - case 'app': { - appconfig.customtextsApp.defList[keyId].defaultvalue = newTexts[ctKey]; - break; - } - case 'login': { - appconfig.customtextsLogin.defList[keyId].defaultvalue = newTexts[ctKey]; - break; - } - case 'booklet': { - appconfig.customtextsBooklet.defList[keyId].defaultvalue = newTexts[ctKey]; - break; + public setDefaultCustomtexts(newTexts: {[key: string]: string;}) { + if (newTexts) { + for (const ctKey of Object.keys(newTexts)) { + const sepIndex = ctKey.indexOf(customtextKeySeparator); + if (sepIndex > 1) { + const keyPrefix = ctKey.slice(0 , sepIndex - 1); + const keyId = ctKey.slice(sepIndex + 1); + + switch (keyPrefix) { + case 'app': { + appconfig.customtextsApp.defList[keyId].defaultvalue = newTexts[ctKey]; + break; + } + case 'login': { + appconfig.customtextsLogin.defList[keyId].defaultvalue = newTexts[ctKey]; + break; + } + case 'booklet': { + appconfig.customtextsBooklet.defList[keyId].defaultvalue = newTexts[ctKey]; + break; + } } } } diff --git a/src/app/start/start.component.html b/src/app/start/start.component.html index b85a75db65caade77c30f97a3fd7d7ea30d6baba..c7b18b1fa9143f8edc1b2d19da7c29de525e1c52 100644 --- a/src/app/start/start.component.html +++ b/src/app/start/start.component.html @@ -1,6 +1,6 @@ <div class="logo"> <a [routerLink]="['/']"> - <img src="assets/IQB-LogoA.png" matTooltip="Startseite"/> + <img src="assets/IQB-LogoA.png" matTooltip="Startseite" alt=""IQB-Logo/> </a> </div> <div class="page-body"> @@ -13,14 +13,14 @@ <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="showLoginForm"> <!-- - - - - - - - - - - - - - - - - --> - <form [formGroup]="testtakerloginform" (ngSubmit)="testtakerlogin()"> + <form [formGroup]="testtakerloginform" (ngSubmit)="login()"> <mat-card-title>Anmelden</mat-card-title> <mat-card-content fxLayout="column"> <mat-form-field> <input matInput formControlName="testname" placeholder="Anmeldename" (keyup.enter)="pw.focus()"> </mat-form-field> <mat-form-field> - <input matInput #pw type="password" formControlName="testpw" placeholder="Kennwort" (keyup.enter)="testtakerlogin()"> + <input matInput #pw type="password" formControlName="testpw" placeholder="Kennwort" (keyup.enter)="login()"> </mat-form-field> </mat-card-content> <mat-card-actions> @@ -51,7 +51,7 @@ <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="showBookletButtons"> <mat-card-title>{{ bookletSelectTitle }}</mat-card-title> <mat-card-content> - <div fxLayout="row" fxLayoutGap="10px" fxLayout="column"> + <div fxLayoutGap="10px" fxLayout="column"> <p *ngIf="bookletlist.length === 0"> Für diese Anmeldung wurde kein Test gefunden. </p> @@ -79,7 +79,26 @@ </mat-card-content> </mat-card> - <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> + <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="showAdminSelection"> + <mat-card-title>Studie wählen</mat-card-title> + <mat-card-content> + <div fxLayoutGap="10px" fxLayout="column"> + <p *ngIf="(mds.loginData$ | async)?.workspaces.length === 0"> + Sie sind mit Administrator-Funktionen angemeldet. Aktuell sind keine Studien für Sie freigegeben. + </p> + <button mat-raised-button color="primary" (click)="buttonGotoWorkspace(ws)" + *ngFor="let ws of (mds.loginData$ | async)?.workspaces"> + {{ws.name}} + </button> + </div> + </mat-card-content> + <mat-card-actions> + <button mat-raised-button color="foreground" *ngIf="(mds.loginData$ | async)?.is_superadmin" [routerLink]="['/superadmin']">Nutzer/Arbeitsbereiche</button> + <button mat-raised-button color="foreground" (click)="resetLogin()">Neu anmelden</button> + </mat-card-actions> + </mat-card> + + <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> <mat-card fxFlex="0 2 400px" fxLayout="column" class="status"> <mat-card-title>{{ 'app_title' | customtext:'app_title':cts.updateCount }}</mat-card-title> diff --git a/src/app/start/start.component.ts b/src/app/start/start.component.ts index c8a77cf1ad262d21260b718805f5842c0d59c636..3a21184c371af645f9e27acc2b49ec3fb2920c28 100644 --- a/src/app/start/start.component.ts +++ b/src/app/start/start.component.ts @@ -3,7 +3,7 @@ import { Subscription, forkJoin } from 'rxjs'; import {CustomtextService, MessageDialogComponent, MessageDialogData, MessageType, ServerError} from 'iqb-components'; import { MatDialog } from '@angular/material'; import { BackendService } from '../backend.service'; -import { PersonTokenAndBookletDbId, LoginData } from '../app.interfaces'; +import {PersonTokenAndBookletDbId, LoginData, WorkspaceData} from '../app.interfaces'; import { Router } from '@angular/router'; import { Component, OnInit, OnDestroy } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; @@ -21,6 +21,7 @@ export class StartComponent implements OnInit, OnDestroy { public showBookletButtons = false; public bookletlist: StartButtonData[] = []; public showTestRunningButtons = false; + public showAdminSelection = false; private loginDataSubscription: Subscription = null; @@ -52,8 +53,21 @@ export class StartComponent implements OnInit, OnDestroy { ngOnInit() { this.loginDataSubscription = this.mds.loginData$.subscribe(logindata => { this.bookletlist = []; - if (logindata.logintoken.length > 0) { + if (logindata.admintoken.length > 0) { + this.showLoginForm = false; + this.showAdminSelection = true; + this.showCodeForm = false; + this.showBookletButtons = false; + this.showTestRunningButtons = false; + this.loginStatusText = []; + this.loginStatusText.push('Admin-Bereich '); + this.loginStatusText.push('angemeldet als ' + logindata.loginname); + if (logindata.is_superadmin) { + this.loginStatusText.push('Rechte auch für Anlegen/Löschen von Nutzern und Workspaces'); + } + } else if (logindata.logintoken.length > 0) { // Statustext box + this.showAdminSelection = false; this.loginStatusText = []; this.loginStatusText.push('Studie: ' + logindata.workspaceName); this.loginStatusText.push('angemeldet als "' + @@ -171,6 +185,7 @@ export class StartComponent implements OnInit, OnDestroy { this.loginStatusText = ['nicht angemeldet']; this.showBookletButtons = false; this.showCodeForm = false; + this.showAdminSelection = false; this.showLoginForm = true; this.showTestRunningButtons = false; } @@ -188,14 +203,14 @@ export class StartComponent implements OnInit, OnDestroy { } // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - testtakerlogin() { + login() { this.dataLoading = true; this.bs.login(this.testtakerloginform.get('testname').value, this.testtakerloginform.get('testpw').value).subscribe( loginData => { if (loginData instanceof ServerError) { const e = loginData as ServerError; this.mds.globalErrorMsg$.next(e); - this.mds.setCustomtextsFromDefList(appconfig.customtextsLogin); + this.mds.addCustomtextsFromDefList(appconfig.customtextsLogin); // no change in other data } else { this.mds.globalErrorMsg$.next(null); @@ -275,6 +290,14 @@ export class StartComponent implements OnInit, OnDestroy { this.mds.endBooklet(); } + buttonGotoWorkspace(ws: WorkspaceData) { + if (ws.role === 'MO') { + this.router.navigateByUrl('/admin/' + ws.id.toString() + '/monitor'); + } else { + this.router.navigateByUrl('/admin/' + ws.id.toString() + '/files'); + } + } + // % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % ngOnDestroy() { if (this.loginDataSubscription !== null) { diff --git a/src/app/superadmin/superadmin-routing.module.ts b/src/app/superadmin/superadmin-routing.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..d950d3690c7bd000b0469aacc6b937c32bae6887 --- /dev/null +++ b/src/app/superadmin/superadmin-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import {SuperadminComponent} from "./superadmin.component"; + + +const routes: Routes = [ + { + path: '', + component: SuperadminComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class SuperadminRoutingModule { } diff --git a/src/app/superadmin/superadmin.component.css b/src/app/superadmin/superadmin.component.css new file mode 100644 index 0000000000000000000000000000000000000000..6da29efa857cb699c343691414dd2ccda9286bfb --- /dev/null +++ b/src/app/superadmin/superadmin.component.css @@ -0,0 +1,4 @@ +p { + margin: 10px; + color: aliceblue; +} diff --git a/src/app/superadmin/superadmin.component.html b/src/app/superadmin/superadmin.component.html new file mode 100644 index 0000000000000000000000000000000000000000..04ba540ada44067ff58dd17c42edfa52d094c846 --- /dev/null +++ b/src/app/superadmin/superadmin.component.html @@ -0,0 +1,4 @@ +<p>SuperadminComponent</p> +<p>Eingeloggt als {{ (mds.loginData$ | async)?.loginname}}</p> +<p> </p> +<p>Sorry - die Einbindung der Administrator-Funktionen in das Testcenter ist noch in Arbeit.</p> diff --git a/src/app/superadmin/superadmin.component.spec.ts b/src/app/superadmin/superadmin.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f81c64d011ad02b5c14497860bef5b5455066eea --- /dev/null +++ b/src/app/superadmin/superadmin.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SuperadminComponent } from './superadmin.component'; + +describe('SuperadminComponent', () => { + let component: SuperadminComponent; + let fixture: ComponentFixture<SuperadminComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SuperadminComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SuperadminComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/superadmin/superadmin.component.ts b/src/app/superadmin/superadmin.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..31822a1221fb46772c56b0741b29e6fead36cf06 --- /dev/null +++ b/src/app/superadmin/superadmin.component.ts @@ -0,0 +1,17 @@ +import { Component, OnInit } from '@angular/core'; +import {MainDataService} from "../maindata.service"; + +@Component({ + templateUrl: './superadmin.component.html', + styleUrls: ['./superadmin.component.css'] +}) +export class SuperadminComponent implements OnInit { + + constructor( + public mds: MainDataService + ) { } + + ngOnInit() { + } + +} diff --git a/src/app/superadmin/superadmin.module.ts b/src/app/superadmin/superadmin.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..42de5ceb17cb199f2d9db3c83516058a3b767d8c --- /dev/null +++ b/src/app/superadmin/superadmin.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { SuperadminRoutingModule } from './superadmin-routing.module'; +import { SuperadminComponent } from './superadmin.component'; + + +@NgModule({ + declarations: [SuperadminComponent], + imports: [ + CommonModule, + SuperadminRoutingModule + ], + exports: [ + SuperadminComponent + ] +}) +export class SuperadminModule { } diff --git a/src/app/sys-check/start.component.html b/src/app/sys-check/start.component.html index c4b9e78de787f89d799ee17731e0ff8f85700413..ca7c04bde32b020add633adf0cfd0dbc78b38d43 100644 --- a/src/app/sys-check/start.component.html +++ b/src/app/sys-check/start.component.html @@ -9,7 +9,7 @@ <div class="page-body"> <div fxLayout="row" fxLayoutAlign="center start"> <mat-card fxFlex="0 2 500px"> - <mat-card-title>System-Check: Starten {{ 'app_title' | customtext:'app_title':cts.updateCount }}</mat-card-title> + <mat-card-title>{{ 'app_title' | customtext:'app_title':cts.updateCount }}: System-Check</mat-card-title> <mat-card-content> <p>Hier können Sie ermitteln, ob das Computersystem, das Sie gerade benutzen, für die hier vorgesehenen Testungen geeignet ist.</p> diff --git a/src/app/sys-check/start.component.ts b/src/app/sys-check/start.component.ts index 15e0d60560e2e482de9947329d022531256abdcb..335fbb6f004a4cdb9ec64f538a624b54887a2e61 100644 --- a/src/app/sys-check/start.component.ts +++ b/src/app/sys-check/start.component.ts @@ -27,7 +27,6 @@ export class StartComponent implements OnInit { this.dataLoading = true; this.bs.getCheckConfigs().subscribe(myConfigs => { this.checkConfigList = myConfigs; - console.log(this.cts.getCustomText('app_title', 'the app-title')); this.dataLoading = false; }); } diff --git a/src/app/sys-check/unit-check/unit-check.component.ts b/src/app/sys-check/unit-check/unit-check.component.ts index 4b653c3f2531373f13c53f4b11656beb954732ed..25f734c2a74e1afb3301696e0b62d300e3d26e41 100644 --- a/src/app/sys-check/unit-check/unit-check.component.ts +++ b/src/app/sys-check/unit-check/unit-check.component.ts @@ -7,6 +7,8 @@ import { Subscription, BehaviorSubject, combineLatest} from 'rxjs'; import { UnitData } from '../sys-check.interfaces'; import { ServerError } from 'iqb-components'; +declare var srcDoc: any; + @Component({ selector: 'iqb-unit-check', templateUrl: './unit-check.component.html', @@ -168,11 +170,13 @@ export class UnitCheckComponent implements OnInit, OnDestroy { private createPlayerElement(playerCode: string): void { this.iFrameItemplayer = <HTMLIFrameElement>document.createElement('iframe'); - this.iFrameItemplayer.setAttribute('srcdoc', playerCode); + // this.iFrameItemplayer.setAttribute('srcdoc', playerCode); this.iFrameItemplayer.setAttribute('sandbox', 'allow-forms allow-scripts allow-same-origin'); this.iFrameItemplayer.setAttribute('class', 'unitHost'); this.iFrameItemplayer.setAttribute('height', '100'); this.iFrameHostElement.nativeElement.appendChild(this.iFrameItemplayer); + srcDoc.set(this.iFrameItemplayer, playerCode); + } ngOnDestroy() { diff --git a/src/app/test-controller/test-controller.component.ts b/src/app/test-controller/test-controller.component.ts index 4a217e0694642820712b93fed605604671d7b569..6ee45dce9eb849e7077c572f9c730415ea737fc9 100644 --- a/src/app/test-controller/test-controller.component.ts +++ b/src/app/test-controller/test-controller.component.ts @@ -480,7 +480,7 @@ export class TestControllerComponent implements OnInit, OnDestroy { if (myData instanceof ServerError) { const e = myData as ServerError; this.mds.globalErrorMsg$.next(e); - this.mds.setCustomtextsFromDefList(appconfig.customtextsBooklet); + this.mds.addCustomtextsFromDefList(appconfig.customtextsBooklet); this.tcs.dataLoading = false; } else { const bookletData = myData as BookletData; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index df10906299fa3fa39d3ebc3ae178258620967f7e..59bddd5fde1ba9790a98f38f0a008e206215323c 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -5,5 +5,5 @@ export const environment = { testcenterUrl: '/', appName: 'IQB-Testcenter', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', - appVersion: '1.5.2 - 12.2.2020' + appVersion: '1.5.3 - 20.2.2020' };