diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts index 5e00460d628210fd17e991922a80cb2c2562656e..539b8fa77f8a0536a3fabd56b2f673c0fd249752 100644 --- a/src/app/about/about.component.ts +++ b/src/app/about/about.component.ts @@ -1,4 +1,3 @@ -import { MainDatastoreService } from './../admin/maindatastore.service'; import { Component, Inject, OnInit } from '@angular/core'; import { BackendService } from './../backend.service'; @@ -11,15 +10,13 @@ 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 mds: MainDatastoreService, - private bs: BackendService + @Inject('APP_VERSION') private appVersion: string ) { } ngOnInit() { - this.mds.pageTitle$.next(''); - this.bs.getAboutText().subscribe(t => this.myAboutText = t as string); + // this.mds.pageTitle$.next(''); + // this.bs.getAboutText().subscribe(t => this.myAboutText = t as string); } - + } diff --git a/src/app/admin/admin.component.css b/src/app/admin/admin.component.css deleted file mode 100644 index bf36bee4044984762fe688a7c28c57f4ae274265..0000000000000000000000000000000000000000 --- a/src/app/admin/admin.component.css +++ /dev/null @@ -1,13 +0,0 @@ -.adminbackground { - flex: 10 0 900px; - box-shadow: 5px 10px 20px black; - background-color: white; - min-height: 85%; - margin: 15px; - padding: 25px; -} - -.communication-error-message { - color: red; - padding: 10px 50px; -} diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html deleted file mode 100644 index 7bd71495d56741e011e998afa5b2eb55c2d89260..0000000000000000000000000000000000000000 --- a/src/app/admin/admin.component.html +++ /dev/null @@ -1,18 +0,0 @@ -<div class="page-body"> - <div class="adminbackground"> - - <p class="communication-error-message" *ngIf="!isAdmin">{{ notLoggedInMessage }}</p> - - <nav mat-tab-nav-bar *ngIf="isAdmin"> - <a mat-tab-link - *ngFor="let link of navLinks" - [routerLink]="link.path" - routerLinkActive #rla="routerLinkActive" - [active]="rla.isActive"> - {{link.label}} - </a> - </nav> - - <router-outlet></router-outlet> - </div> -</div> diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts deleted file mode 100644 index e65b467c86b131aa84a9947567f5e6cc408b0988..0000000000000000000000000000000000000000 --- a/src/app/admin/admin.component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Observable, BehaviorSubject } from 'rxjs'; -import { Component, OnInit } from '@angular/core'; -import { MatTabsModule, MatSelectModule, MatFormFieldModule } from '@angular/material'; -import { MainDatastoreService } from './maindatastore.service'; -import { WorkspaceData } from './backend.service'; - - -@Component({ - templateUrl: './admin.component.html', - styleUrls: ['./admin.component.css'] -}) -export class AdminComponent implements OnInit { - public navLinks = [ - {path: 'myfiles', label: 'Dateien'}, - {path: 'syscheck', label: 'System-Check Berichte'}, - {path: 'monitor', label: 'Monitor'}, - {path: 'results', label: 'Ergebnisse'} - ]; - - private isAdmin = false; - private notLoggedInMessage = ''; - - // CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC - constructor( - private mds: MainDatastoreService - ) { - this.mds.isAdmin$.subscribe(is => this.isAdmin = is); - this.mds.notLoggedInMessage$.subscribe(msg => { - if ((msg === null) || (msg.length === 0)) { - this.notLoggedInMessage = 'Bitte anmelden!'; - } else { - this.notLoggedInMessage = msg; - } - }); - } - - // CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC - ngOnInit() { - this.mds.workspaceId$.subscribe(id => { - console.log('enter ' + id); - if (id >= 0) { - const workspaceList = this.mds.workspaceList$.getValue(); - if (workspaceList.length > 0) { - for (let i = 0; i < workspaceList.length; i++) { - console.log('check: ' + workspaceList[i].id); - if (workspaceList[i].id == id) { - console.log('got'); - this.mds.updatePageTitle('IQB-Testcenter Verwaltung: ' + workspaceList[i].name); - break; - } - } - } - } else { - this.mds.updatePageTitle('IQB-Testcenter Verwaltung...'); - } - }) - } -} diff --git a/src/app/admin/backend.service.ts b/src/app/admin/backend.service.ts deleted file mode 100644 index 786ed307be2b3f376d8c0748f4672dd432e7049c..0000000000000000000000000000000000000000 --- a/src/app/admin/backend.service.ts +++ /dev/null @@ -1,385 +0,0 @@ -import { Injectable, Inject } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; -import { Observable, throwError } from 'rxjs'; -// import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { catchError } from 'rxjs/operators'; - -@Injectable() -export class BackendService { - public get serverUrl():string { - return this._serverUrl; - } - - constructor( - @Inject('SERVER_URL') private _serverUrl: string, - private http: HttpClient) { - this._serverUrl = this._serverUrl + 'admin/php_admin/'; - } - - private errorHandler(error: Error | any): Observable<any> { - return Observable.throw(error); - } - - // ******************************************************************* - login(name: string, password: string): Observable<LoginStatusResponseData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<LoginStatusResponseData>(this._serverUrl + 'loginAdmin.php', {n: name, p: password}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // ******************************************************************* - logout(token: string): Observable<boolean | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<boolean>(this._serverUrl + 'logoutAdmin.php', {at: token}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // ******************************************************************* - getStatus(token: string): Observable<LoginStatusResponseData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<LoginStatusResponseData>(this._serverUrl + 'getStatusAdmin.php', {at: token}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - - // ******************************************************************* - // Fehlerbehandlung beim Aufrufer - getFile(token: string, workspaceId: number, filetype: string, filename: string): Observable<GetFileResponseData[] | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<GetFileResponseData[]>(this._serverUrl + 'getFile.php', { - at: token, - ws: workspaceId, - ft: filetype, - fn: filename - }, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // ******************************************************************* - // Fehlerbehandlung beim Aufrufer - getFiles(token: string, workspaceId: number): Observable<GetFileResponseData[] | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<GetFileResponseData[]>(this._serverUrl + 'getFileList.php', {at: token, ws: workspaceId}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // ******************************************************************* - // Fehlerbehandlung beim Aufrufer - deleteFiles(token: string, workspaceId: number, filesToDelete: Array<string>): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<string>(this._serverUrl + 'deleteFiles.php', {at: token, ws: workspaceId, f: filesToDelete}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - // ******************************************************************* - checkWorkspace(token: string, workspaceId: number): Observable<CheckWorkspaceResponseData | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<CheckWorkspaceResponseData>(this._serverUrl + 'checkWorkspace.php', {at: token, ws: workspaceId}, httpOptions) - .pipe( - catchError(this.handleError) - ); - } - - /*******************************/ - getBookletsStarted(adminToken: string, workspaceId: number, groups: string[]): Observable<BookletsStarted[]>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<BookletsStarted[]>(this._serverUrl + 'getBookletsStarted.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - /*******************************/ - lockBooklets(adminToken: string, workspaceId: number, groups: string[]): Observable<boolean>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<boolean>(this._serverUrl + 'lockBooklets.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - unlockBooklets(adminToken: string, workspaceId: number, groups: string[]): Observable<boolean>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<boolean>(this._serverUrl + 'unlockBooklets.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - getMonitorData(adminToken: string, workspaceId: number): Observable<MonitorData[]>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<MonitorData[]>(this._serverUrl + 'getMonitorData.php', {at: adminToken, ws: workspaceId}, httpOptions); - } - - getResultData(adminToken: string, workspaceId: number): Observable<ResultData[]>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<ResultData[]>(this._serverUrl + 'getResultData.php', {at: adminToken, ws: workspaceId}, httpOptions); - } - - getResponses(adminToken: string, workspaceId: number, groups: string[]): Observable<UnitResponse[]>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<UnitResponse[]>(this._serverUrl + 'getResponses.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - getLogs(adminToken: string, workspaceId: number, groups: string[]): Observable<LogData[]>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<LogData[]>(this._serverUrl + 'getLogs.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - getReviews(adminToken: string, workspaceId: number, groups: string[]): Observable<ReviewData[]>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<ReviewData[]>(this._serverUrl + 'getReviews.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - deleteData(adminToken: string, workspaceId: number, groups: string[]): Observable<boolean>{ - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<boolean>(this._serverUrl + 'deleteData.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); - } - - getSysCheckReportList(adminToken: string, workspaceId: number): Observable<SysCheckStatistics[]> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<SysCheckStatistics[]>(this._serverUrl + 'getSysCheckReportList.php', {at: adminToken, ws: workspaceId}, httpOptions); - } - - getSysCheckReport(adminToken: string, workspaceId: number, reports: string[], columnDelimiter: string, quoteChar: string): Observable<string[]> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<string[]>(this._serverUrl + 'getSysCheckReport.php', - {at: adminToken, ws: workspaceId, r: reports, cd: columnDelimiter, q: quoteChar}, httpOptions); - } - - deleteSysCheckReports(adminToken: string, workspaceId: number, reports: string[]): Observable<boolean> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return this.http - .post<boolean>(this._serverUrl + 'deleteSysCheckReports.php', - {at: adminToken, ws: workspaceId, r: reports}, httpOptions); - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - 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 LoginStatusResponseData { - admintoken: string; - name: string; - workspaces: WorkspaceData[]; - is_superadmin: boolean; -} - -export interface WorkspaceData { - id: number; - name: string; -} - -export interface ServerError { - code: number; - label: string; -} - -export interface GetFileResponseData { - filename: string; - filesize: number; - filesizestr: string; - filedatetime: string; - filedatetimestr: string; - type: string; - typelabel: string; - isChecked: boolean; -} - -export interface CheckWorkspaceResponseData { - errors: string[]; - infos: string[]; - warnings: string[]; -} - - -export interface GroupResponse { - name: string; - testsTotal: number; - testsStarted: number; - responsesGiven: number; -} - -export interface BookletsStarted { - groupname: string; - loginname: string; - code: string; - bookletname: string; - locked: boolean; -} - -export interface UnitResponse { - groupname: string; - loginname: string; - code: string; - bookletname: string; - unitname: string; - responses: string; - restorepoint: string; - responsetype: string; - responses_ts: number; - restorepoint_ts: number; - laststate: string; -} - -export interface MonitorData { - groupname: string; - loginsPrepared: number; - personsPrepared: number; - bookletsPrepared: number; - bookletsStarted: number; - bookletsLocked: number; -} - -export interface ResultData { - groupname: string; - bookletsStarted: number; - num_units_min: number; - num_units_max: number; - num_units_mean: number; -} - -export interface LogData { - groupname: string; - loginname: string; - code: string; - bookletname: string; - unitname: string; - timestamp: number; - logentry: string; -} - -export interface ReviewData { - groupname: string; - loginname: string; - code: string; - bookletname: string; - unitname: string; - priority: number; - categories: string; - reviewtime: Date; - entry: string; -} - -export interface SysCheckStatistics { - id: string; - label: string; - count: number; - details: string[]; -} diff --git a/src/app/admin/index.ts b/src/app/admin/index.ts deleted file mode 100644 index 954269bd8909086ae2d41184550b728f44540ed7..0000000000000000000000000000000000000000 --- a/src/app/admin/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { MainDatastoreService } from './maindatastore.service'; -export { AdminModule } from './admin.module'; -export { AdminComponent } from './admin.component'; - diff --git a/src/app/admin/maindatastore.service.spec.ts b/src/app/admin/maindatastore.service.spec.ts deleted file mode 100644 index 08981a462da14d6b8688e13fdd1cfdc7482712f2..0000000000000000000000000000000000000000 --- a/src/app/admin/maindatastore.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { MainDatastoreService } from './maindatastore.service'; - -describe('StatusService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [MainDatastoreService] - }); - }); - - it('should be created', inject([MainDatastoreService], (service: MainDatastoreService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/src/app/admin/maindatastore.service.ts b/src/app/admin/maindatastore.service.ts deleted file mode 100644 index 701eb2ff54b4029b39d759d33a97c3eb7ecf940b..0000000000000000000000000000000000000000 --- a/src/app/admin/maindatastore.service.ts +++ /dev/null @@ -1,156 +0,0 @@ -// import { Observable } from 'rxjs/Observable'; -// import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { BehaviorSubject } from 'rxjs'; -import { FormGroup } from '@angular/forms'; -import { Injectable, Component, Input, Output, EventEmitter } from '@angular/core'; -import { MatDialog, MatDialogRef } from '@angular/material'; -import { Router, ActivatedRoute } from '@angular/router'; - -import { IqbCommonModule, ConfirmDialogComponent, ConfirmDialogData } from '../iqb-common'; -import { BackendService, LoginStatusResponseData, WorkspaceData, ServerError } from './backend.service'; - -@Injectable({ - providedIn: 'root' -}) - -export class MainDatastoreService { - public pageTitle$ = new BehaviorSubject('Teststart'); - public isAdmin$ = new BehaviorSubject<boolean>(false); - public loginName$ = new BehaviorSubject<string>(''); - public workspaceId$ = new BehaviorSubject<number>(-1); - public workspaceList$ = new BehaviorSubject<WorkspaceData[]>([]); - public notLoggedInMessage$ = new BehaviorSubject<string>(''); - public adminToken$ = new BehaviorSubject<string>(''); - public isSuperadmin$ = new BehaviorSubject<boolean>(false); - - // ................................................................................. - private _lastloginname = ''; - - // ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc - constructor ( - public loginDialog: MatDialog, - public confirmDialog: MatDialog, - private bs: BackendService, - private route: ActivatedRoute, - private router: Router - ) { - let myToken = localStorage.getItem('at'); - if ((myToken === null) || (myToken === undefined)) { - myToken = ''; - } else { - this.bs.getStatus(myToken).subscribe( - (admindata: LoginStatusResponseData) => { - this.updateAdminStatus(admindata.admintoken, admindata.name, admindata.workspaces, admindata.is_superadmin, ''); - }, (err: ServerError) => { - this.updateAdminStatus('', '', [], false, err.label); - }); - } - } - - // ******************************************************************************************************* - login(name: string, password: string) { - this.bs.login(name, password).subscribe( - (admindata: LoginStatusResponseData) => { - this.updateAdminStatus(admindata.admintoken, admindata.name, admindata.workspaces,admindata.is_superadmin, ''); - // this.route.url.subscribe(segments => { - // const segmentsStr = segments.join(''); - // if (segmentsStr.indexOf('/admin') < 0) { - // this.router.navigateByUrl('/admin'); - // } - // }); - }, (err: ServerError) => { - this.updateAdminStatus('', '', [], false, err.label); - } - ); - } - - // ******************************************************************************************************* - logout() { - const dialogRef = this.confirmDialog.open(ConfirmDialogComponent, { - width: '400px', - height: '300px', - data: <ConfirmDialogData>{ - title: 'Abmelden', - content: 'Möchten Sie sich abmelden?', - confirmbuttonlabel: 'Abmelden' - } - }); - dialogRef.afterClosed().subscribe(result => { - if (result !== false) { - this.bs.logout(this.adminToken$.getValue()).subscribe( - logoutresponse => { - this.updateAdminStatus('', '', [], false, ''); - this.router.navigateByUrl('/'); - }, (err: ServerError) => { - this.updateAdminStatus('', '', [], false, err.label); - this.router.navigateByUrl('/'); - } - ); - } - }); - } - - // ******************************************************************************************************* - updatePageTitle(newTitle: string) { - this.pageTitle$.next(newTitle); - } - updateWorkspaceId(newId: number) { - this.workspaceId$.next(newId); - localStorage.setItem('ws', String(newId)); - } - updateAdminStatus(token: string, name: string, workspaces: WorkspaceData[], is_superadmin: boolean, message: string) { - if ((token === null) || (token.length === 0)) { - this.isAdmin$.next(false); - localStorage.removeItem('at'); - this.adminToken$.next(''); - this.workspaceId$.next(-1); - this.isSuperadmin$.next(false); - this.workspaceList$.next([]); - this.loginName$.next(''); - this.notLoggedInMessage$.next(message); - } else { - this.isAdmin$.next(true); - localStorage.setItem('at', token); - this.adminToken$.next(token); - if (workspaces.length > 0) { - workspaces.sort((ws1, ws2) => { - if (ws1.name > ws2.name) { - return 1 - } else if (ws1.name < ws2.name) { - return -1 - } else { - return 0; - } - }) - } - this.workspaceList$.next(workspaces); - this.loginName$.next(name); - this.isSuperadmin$.next(is_superadmin); - this.notLoggedInMessage$.next(''); - - // set valid workspace-id - - const wsIdStr = localStorage.getItem('ws'); - let wsId = -1; - if (wsIdStr !== null) { - wsId = +wsIdStr; - if (workspaces.length > 0) { - let newWsId = workspaces[0]['id']; - for (let i = 0; i < workspaces.length; i++) { - // id comes as string - if (+workspaces[i]['id'] === wsId) { - newWsId = wsId; - break; - } - } - this.updateWorkspaceId(newWsId); - } else { - this.updateWorkspaceId(-1); - } - } else { - this.updateWorkspaceId(-1); - } - } - } -} - diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c8bc885bca20e332ec6217e5eb06f64fd98faae7..e4c0b4dd5c0115ee4a5722578e35cc2672d7ba76 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,15 +1,15 @@ import { AboutComponent } from './about/about.component'; import { SuperadminComponent } from './superadmin/superadmin.component'; -import { HomeComponent } from './home/home.component'; +import { StartComponent } from './start/start.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { AdminComponent } from './admin'; +import { WorkspaceComponent } from './workspace'; const routes: Routes = [ - {path: '', redirectTo: 'home', pathMatch: 'full'}, - {path: 'home', component: HomeComponent}, - {path: 'admin', component: AdminComponent}, + {path: '', redirectTo: 'start', pathMatch: 'full'}, + {path: 'start', component: StartComponent}, + {path: 'ws', component: WorkspaceComponent}, {path: 'about', component: AboutComponent}, {path: 'superadmin', component: SuperadminComponent} ]; diff --git a/src/app/app.component.html b/src/app/app.component.html deleted file mode 100644 index 126e75b27ee2e9022d7cd66c297c3b97536ca827..0000000000000000000000000000000000000000 --- a/src/app/app.component.html +++ /dev/null @@ -1,13 +0,0 @@ -<mat-toolbar> - <a [routerLink]="['/']"> - <img class="logo" src="assets/IQB-LogoA.png" matTooltip="Startseite"/> - </a> - <span>{{ title }}</span> - <span class="itp-fill-remaining-space"></span> - - <button mat-icon-button *ngIf="isLoggedIn" (click)="logout()"> - <mat-icon>exit_to_app</mat-icon> - </button> -</mat-toolbar> - -<router-outlet></router-outlet> diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fc7a113919ad6cb95f60b51dce8c2b46b773ff35..d7271ddb94f0a47c5a44f87047b180b95a8ab731 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,46 +1,39 @@ -import { environment } from '../environments/environment'; -import { Router } from '@angular/router'; +import { LoginData } from './app.interfaces'; +import { BackendService, ServerError } from './backend.service'; import { Component, OnInit } from '@angular/core'; -import { MatDialog, MatDialogRef } from '@angular/material'; -import { FormGroup } from '@angular/forms'; - -import { LoginStatusResponseData } from './admin/backend.service'; -import { MainDatastoreService } from './admin'; -import { IqbCommonModule, ConfirmDialogComponent, ConfirmDialogData } from './iqb-common'; +import { MainDataService } from './maindata.service'; @Component({ selector: 'app-root', - templateUrl: './app.component.html', + template: `<router-outlet></router-outlet>`, styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { - public title = ''; - public isLoggedIn = false; - public isSuperadmin = false; constructor ( - private mds: MainDatastoreService, - private router: Router, - public aboutDialog: MatDialog) { } + private mds: MainDataService, + private bs: BackendService) { } ngOnInit() { - this.mds.isAdmin$.subscribe( - is => this.isLoggedIn = is); - - this.mds.pageTitle$.subscribe( - t => this.title = t); - - this.mds.isSuperadmin$.subscribe( - is => this.isSuperadmin = is); - + const adminToken = localStorage.getItem('at'); + if (adminToken !== null) { + if (adminToken.length > 0) { + this.bs.getLoginData(adminToken).subscribe( + (admindata: LoginData) => { + this.mds.setNewLoginData(admindata); + }, (err: ServerError) => { + this.mds.setNewLoginData(); + console.log(err); + this.mds.globalErrorMsg$.next(err); + } + ); + } else { + this.mds.setNewLoginData(); + } + } else { + this.mds.setNewLoginData(); + } } - - - // ******************************************************************************************************* - logout() { - this.mds.logout(); - } - } diff --git a/src/app/app.interceptor.ts b/src/app/app.interceptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3db39b83e0213829dccb09f63f6decb2175d23b --- /dev/null +++ b/src/app/app.interceptor.ts @@ -0,0 +1,34 @@ +import { MainDataService } from './maindata.service'; +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, + HttpHandler, HttpEvent, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + constructor(public mds: MainDataService) {} + + intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { + let authDataStr = request.headers.get('AuthToken'); + let authData = {}; + if (authDataStr) { + authData = JSON.parse(authDataStr); + } + + const loginData = this.mds.loginData$.getValue(); + if (loginData !== null) { + authData['at'] = loginData.admintoken; + } + const requestA = request.clone({ + setHeaders: { + AuthToken: JSON.stringify(authData) + } + }); + + return next.handle(requestA); + } +} + +export const httpInterceptorProviders = [ + { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, +]; diff --git a/src/app/app.interfaces.ts b/src/app/app.interfaces.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b1568e07e768d6139e7620a32d1c47f053bb274 --- /dev/null +++ b/src/app/app.interfaces.ts @@ -0,0 +1,12 @@ +export interface WorkspaceData { + id: number; + name: string; + role: string; +} + +export interface LoginData { + admintoken: string; + name: string; + workspaces: WorkspaceData[]; + is_superadmin: boolean; +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 949711c455ed986e9ad91547a5f22f766482d744..fe0a9212a9d702d067c315dbd97e03e1984746f0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -14,15 +14,16 @@ import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; import { IqbCommonModule } from './iqb-common'; -import { AdminModule } from './admin'; -import { HomeComponent } from './home/home.component'; +import { WorkspaceModule } from './workspace'; +import { StartComponent } from './start/start.component'; import { SuperadminModule } from './superadmin'; import { FlexLayoutModule } from "@angular/flex-layout"; +import { httpInterceptorProviders } from './app.interceptor'; @NgModule({ declarations: [ AppComponent, - HomeComponent, + StartComponent, AboutComponent ], imports: [ @@ -42,7 +43,7 @@ import { FlexLayoutModule } from "@angular/flex-layout"; MatTabsModule, ReactiveFormsModule, HttpClientModule, - AdminModule, + WorkspaceModule, SuperadminModule, AppRoutingModule, IqbCommonModule, @@ -55,7 +56,8 @@ import { FlexLayoutModule } from "@angular/flex-layout"; provide: LocationStrategy, useClass: HashLocationStrategy }, - BackendService + BackendService, + httpInterceptorProviders ], bootstrap: [AppComponent] }) diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts index 335e03322fb819e6358a0393a013779041164dd4..ee58254a8fc341a7ced5354af12ed0c2fd039dba 100644 --- a/src/app/backend.service.ts +++ b/src/app/backend.service.ts @@ -1,64 +1,94 @@ +import { LoginData } from './app.interfaces'; import { Injectable, Inject } from '@angular/core'; import { HttpClient, HttpHeaders, HttpEvent, HttpErrorResponse } from '@angular/common/http'; -import { Observable, throwError, of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { tokenKey } from '@angular/core/src/view'; +// ============================================================================ +// class instead of interface to be able to use instanceof to check type +export class ServerError { + public code: number; + public labelNice: string; + public labelSystem: string; + constructor(code: number, labelNice: string, labelSystem: string) { + this.code = code; + this.labelNice = labelNice; + this.labelSystem = labelSystem; + } +} + +// ============================================================================ +export class ErrorHandler { + public static handle(errorObj: HttpErrorResponse): Observable<ServerError> { + let myreturn: ServerError = null; + if (errorObj.error instanceof ErrorEvent) { + myreturn = new ServerError(500, 'Verbindungsproblem', (<ErrorEvent>errorObj.error).message); + } else { + myreturn = new ServerError(errorObj.status, 'Verbindungsproblem', errorObj.message); + if (errorObj.status === 401) { + myreturn.labelNice = 'Zugriff verweigert - bitte (neu) anmelden!'; + } else if (errorObj.status === 503) { + myreturn.labelNice = 'Achtung: Server meldet Datenbankproblem.'; + } + } + + return of(myreturn); + } +} @Injectable({ providedIn: 'root' }) export class BackendService { + private serverUrlSlim = ''; + constructor( - @Inject('SERVER_URL') private serverUrl: string, - private http: HttpClient) { - this.serverUrl = this.serverUrl + 'php_start/'; - } + @Inject('SERVER_URL') private serverUrl: string, + private http: HttpClient) { - private errorHandler(error: Error | any): Observable<any> { - return Observable.throw(error); + this.serverUrlSlim = this.serverUrl + 'php/'; + this.serverUrl = this.serverUrl + 'php_start/'; } // ******************************************************************* - getAboutText(): Observable<string | ServerError> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }; - return of('jajajaja'); - // this.http - // .post<string>(this.serverUrl + 'getAboutText.php', httpOptions) - // .pipe( - // catchError(this.handleError) - // ); + login(name: string, password: string): Observable<LoginData | ServerError> { + return this.http + .post<LoginData>(this.serverUrlSlim + 'login.php/login', {n: name, p: password}) + .pipe( + catchError(ErrorHandler.handle) + ); } - 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); + // ******************************************************************* + logout(): Observable<boolean | ServerError> { + return this.http + .post<boolean>(this.serverUrlSlim + 'logout', {}) + .pipe( + catchError(ErrorHandler.handle) + ); } -} + // ******************************************************************* + getLoginData(adminToken: string): Observable<LoginData | ServerError> { + return this.http + .post<LoginData>(this.serverUrlSlim + 'login.php/login', {at: adminToken}) + .pipe( + catchError(ErrorHandler.handle) + ); + } -/////// * Interfaces * /////// + // // ******************************************************************* + // getAboutText(): Observable<string | ServerError> { + // const httpOptions = { + // headers: new HttpHeaders({ + // 'Content-Type': 'application/json' + // }) + // }; + // return this.http + // .post<string>(this.serverUrl + 'getAboutText.php', httpOptions) + // .pipe( + // catchError(this.handleError) + // ); + // } -export interface ServerError { - code: number; - label: string; } - - diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts deleted file mode 100644 index 5e8d9dfa6687137316c7c31207a836dcf4e75f61..0000000000000000000000000000000000000000 --- a/src/app/home/home.component.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { WorkspaceData } from './../admin/backend.service'; -import { MainDatastoreService } from './../admin/maindatastore.service'; -import { Router } from '@angular/router'; -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { FormGroup, FormBuilder, FormArray, FormControl, Validators } from '@angular/forms'; - - -@Component({ - templateUrl: './home.component.html', - styleUrls: ['./home.component.css'] -}) -export class HomeComponent implements OnInit { - adminloginform: FormGroup; - isLoggedIn = false; - isSuperadmin = false; - loginName = ''; - isError = false; - errorMessage = ''; - workspaceList: WorkspaceData[] = []; - - constructor(private fb: FormBuilder, - private mds: MainDatastoreService, - private router: Router) { } - - ngOnInit() { - this.mds.pageTitle$.next(''); - this.mds.isAdmin$.subscribe( - is => this.isLoggedIn = is); - - this.adminloginform = this.fb.group({ - testname: this.fb.control('', [Validators.required, Validators.minLength(3)]), - testpw: this.fb.control('', [Validators.required, Validators.minLength(3)]) - }); - - this.mds.workspaceList$.subscribe(list => this.workspaceList = list); - this.mds.isSuperadmin$.subscribe(is => this.isSuperadmin = is); - this.mds.loginName$.subscribe(n => this.loginName = n); - } - - login() { - this.isError = false; - this.errorMessage = ''; - - if (this.adminloginform.valid) { - this.mds.login(this.adminloginform.get('testname').value, this.adminloginform.get('testpw').value); - } - } - - buttonGotoWorkspace(ws: WorkspaceData) { - this.mds.updateWorkspaceId(ws.id); - this.router.navigateByUrl('/admin'); - } - - changeLogin() { - this.mds.logout(); - } -} diff --git a/src/app/maindata.service.spec.ts b/src/app/maindata.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..16e5e0e6d4e1d7660637c67c77a2cb8ff736d88c --- /dev/null +++ b/src/app/maindata.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { MainDataService } from './maindata.service'; + +describe('MaindataService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: MainDataService = TestBed.get(MainDataService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/maindata.service.ts b/src/app/maindata.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..705612326e11d439c91b03987a7109c4b7853125 --- /dev/null +++ b/src/app/maindata.service.ts @@ -0,0 +1,89 @@ +import { BehaviorSubject } from 'rxjs'; +import { LoginData } from './app.interfaces'; +import { Injectable } from '@angular/core'; +import { ServerError } from './backend.service'; + +@Injectable({ + providedIn: 'root' +}) +export class MainDataService { + private static defaultLoginData: LoginData = { + admintoken: '', + name: '', + workspaces: [], + is_superadmin: false + }; + + public get adminToken() : string { + const myLoginData = this.loginData$.getValue(); + if (myLoginData) { + return myLoginData.admintoken; + } else { + return ''; + } + } + + + public loginData$ = new BehaviorSubject<LoginData>(MainDataService.defaultLoginData); + public globalErrorMsg$ = new BehaviorSubject<ServerError>(null); + + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + setNewLoginData(logindata?: LoginData) { + const myLoginData: LoginData = { + admintoken: MainDataService.defaultLoginData.admintoken, + name: MainDataService.defaultLoginData.name, + workspaces: MainDataService.defaultLoginData.workspaces, + is_superadmin: MainDataService.defaultLoginData.is_superadmin + }; + + if (logindata) { + if ( + (logindata.admintoken.length > 0) && + (logindata.name.length > 0) && + (logindata.workspaces.length > 0)) { + myLoginData.admintoken = logindata.admintoken; + myLoginData.name = logindata.name; + myLoginData.workspaces = logindata.workspaces; + myLoginData.is_superadmin = logindata.is_superadmin; + } + } + this.loginData$.next(myLoginData); + localStorage.setItem('at', myLoginData.admintoken); + } + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + setNewErrorMsg(err: ServerError = null) { + this.globalErrorMsg$.next(err); + } + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + getWorkspaceName(ws: number): string { + let myreturn = ''; + const myLoginData = this.loginData$.getValue(); + if ((myLoginData !== null) && (myLoginData.workspaces.length > 0)) { + for (let i = 0; i < myLoginData.workspaces.length; i++) { + if (myLoginData.workspaces[i].id == ws) { + myreturn = myLoginData.workspaces[i].name; + break; + } + } + } + return myreturn; + } + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + getWorkspaceRole(ws: number): string { + let myreturn = ''; + const myLoginData = this.loginData$.getValue(); + if ((myLoginData !== null) && (myLoginData.workspaces.length > 0)) { + for (let i = 0; i < myLoginData.workspaces.length; i++) { + if (myLoginData.workspaces[i].id == ws) { + myreturn = myLoginData.workspaces[i].role; + break; + } + } + } + return myreturn; + } +} diff --git a/src/app/home/home.component.css b/src/app/start/start.component.css similarity index 98% rename from src/app/home/home.component.css rename to src/app/start/start.component.css index 15f59d6f785f7ab9609031e274b38ee8046c56ac..5b0d7dccc5371ab0350d50bf7bfddadcc41c2715 100644 --- a/src/app/home/home.component.css +++ b/src/app/start/start.component.css @@ -1,6 +1,7 @@ .mat-card { margin: 10px; } + .status { background-color: lightgrey; } diff --git a/src/app/home/home.component.html b/src/app/start/start.component.html similarity index 70% rename from src/app/home/home.component.html rename to src/app/start/start.component.html index 0ad7c1927c6f5ed5fea42e0a501d5b6f87b8acbd..ca7927cae9731221fb83178d00c902414bfc81b7 100644 --- a/src/app/home/home.component.html +++ b/src/app/start/start.component.html @@ -1,3 +1,8 @@ +<div class="logo"> + <a [routerLink]="['/']"> + <img src="assets/IQB-LogoA.png" matTooltip="Startseite"/> + </a> +</div> <div class="page-body"> <div class="spinner-container" *ngIf="dataLoading"> <mat-spinner></mat-spinner> @@ -6,15 +11,15 @@ <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> - <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="!isLoggedIn"> + <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="showLogin"> <!-- - - - - - - - - - - - - - - - - --> <form [formGroup]="adminloginform" (ngSubmit)="login()"> <mat-card-title>Anmelden</mat-card-title> <mat-card-content fxLayout="column"> - <mat-form-field class="full-width"> + <mat-form-field> <input matInput formControlName="testname" placeholder="Anmeldename" (keyup.enter)="pw.focus()"> </mat-form-field> - <mat-form-field class="full-width"> + <mat-form-field> <input matInput #pw type="password" formControlName="testpw" placeholder="Kennwort" (keyup.enter)="login()"> </mat-form-field> </mat-card-content> @@ -22,24 +27,25 @@ <button mat-raised-button type="submit" [disabled]="adminloginform.invalid" color="primary">Weiter</button> </mat-card-actions> </form> + <p class="error-msg">{{ (mds.globalErrorMsg$ | async)?.labelNice }}</p> </mat-card> - <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="isLoggedIn"> + <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="!showLogin"> <mat-card-title>Studie wählen</mat-card-title> <mat-card-content> <div fxLayout="row" fxLayoutGap="10px" fxLayout="column"> - <p *ngIf="workspaceList.length === 0"> + <p *ngIf="(mds.loginData$ | async)?.workspaces.length === 0"> Für diese Anmeldung wurden keine Studien gefunden. </p> <button mat-raised-button color="primary" (click)="buttonGotoWorkspace(ws)" - *ngFor="let ws of workspaceList"> + *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="isSuperadmin" [routerLink]="['/superadmin']">Nutzer/Arbeitsbereiche</button> - <button mat-raised-button color="foreground" (click)="changeLogin()">Anmeldung ändern</button> + <button mat-raised-button color="foreground" *ngIf="(mds.loginData$ | async)?.is_superadmin" [routerLink]="['/superadmin']">Nutzer/Arbeitsbereiche</button> + <button mat-raised-button color="foreground" (click)="mds.setNewLoginData()">Anmeldung ändern</button> </mat-card-actions> </mat-card> @@ -49,20 +55,19 @@ <!-- - - - - - - - - - - - - - - - - --> <mat-card-content> - <div *ngIf="!isLoggedIn"> + <div> <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. Dies ist die Web-Anwendung zur Verwaltung der Testinhalte und -ergebnisse. Der Zugang ist nur möglich, wenn Sie vom IQB Zugangsdaten erhalten haben. Es sind keine weiteren Seiten öffentlich verfügbar.</p> </div> - <div *ngIf="isLoggedIn"> + <div *ngIf="!showLogin"> <ul> - <li>angemeldet als: {{ loginName }}</li> - <li *ngIf="isSuperadmin">Superkräfte, d. h. zum Ändern von Nutzerrechten und Arbeitsbereichen berechtigt</li> + <li>angemeldet als: {{ (mds.loginData$ | async)?.name }}</li> + <li *ngIf="(mds.loginData$ | async)?.is_superadmin">zum Ändern von Nutzerrechten und Arbeitsbereichen berechtigt</li> </ul> </div> - <p>{{ errorMessage }}</p> </mat-card-content> <mat-card-actions> <button mat-raised-button color="foreground" [routerLink]="['/about']">Impressum/Datenschutz</button> diff --git a/src/app/home/home.component.spec.ts b/src/app/start/start.component.spec.ts similarity index 63% rename from src/app/home/home.component.spec.ts rename to src/app/start/start.component.spec.ts index 490e81bdfbc8a6aa1b052fb7d812d3d0bac346a5..10f012b9c1cdefa4501e30a907b3d4dbb54f24cc 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/start/start.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { HomeComponent } from './home.component'; +import { StartComponent } from './start.component'; describe('HomeComponent', () => { - let component: HomeComponent; - let fixture: ComponentFixture<HomeComponent>; + let component: StartComponent; + let fixture: ComponentFixture<StartComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HomeComponent ] + declarations: [ StartComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(HomeComponent); + fixture = TestBed.createComponent(StartComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/start/start.component.ts b/src/app/start/start.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..08b7fb9c24eba4dea0d9066bf2b0778db8467e2a --- /dev/null +++ b/src/app/start/start.component.ts @@ -0,0 +1,61 @@ +import { LoginData, WorkspaceData } from './../app.interfaces'; +import { BackendService, ServerError } from './../backend.service'; +import { MainDataService } from './../maindata.service'; +import { Router } from '@angular/router'; +import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; +import { FormGroup, FormBuilder, FormArray, FormControl, Validators } from '@angular/forms'; +import { Subscription } from 'rxjs'; + + +@Component({ + templateUrl: './start.component.html', + styleUrls: ['./start.component.css'] +}) +export class StartComponent implements OnInit, OnDestroy { + adminloginform: FormGroup; + private loginDataSubscription: Subscription = null; + public showLogin = true; + + constructor(private fb: FormBuilder, + private mds: MainDataService, + private bs: BackendService, + private router: Router) { } + + ngOnInit() { + this.adminloginform = this.fb.group({ + testname: this.fb.control('', [Validators.required, Validators.minLength(3)]), + testpw: this.fb.control('', [Validators.required, Validators.minLength(3)]) + }); + this.loginDataSubscription = this.mds.loginData$.subscribe(logindata => { + this.showLogin = logindata.admintoken.length === 0; + }); + } + + // ******************************************************************************************************* + login() { + if (this.adminloginform.valid) { + this.bs.login( + this.adminloginform.get('testname').value, this.adminloginform.get('testpw').value + ).subscribe(admindata => { + if (admindata instanceof ServerError) { + this.mds.setNewLoginData(); + this.mds.setNewErrorMsg(admindata as ServerError); + } else { + this.mds.setNewLoginData(admindata as LoginData); + this.mds.setNewErrorMsg(); + } + }); + } + } + + buttonGotoWorkspace(ws: WorkspaceData) { + this.router.navigateByUrl('/ws/' + ws.id.toString()); + } + + // % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + ngOnDestroy() { + if (this.loginDataSubscription !== null) { + this.loginDataSubscription.unsubscribe(); + } + } +} diff --git a/src/app/superadmin/about-text/about-text.component.ts b/src/app/superadmin/about-text/about-text.component.ts index acd6ec179f0e34362a65fcfb46fa77243164e176..cece233a96150aa7f73becdc690d209d19dee19d 100644 --- a/src/app/superadmin/about-text/about-text.component.ts +++ b/src/app/superadmin/about-text/about-text.component.ts @@ -8,7 +8,6 @@ import { SelectionModel } from '@angular/cdk/collections'; import { ConfirmDialogComponent, ConfirmDialogData, MessageDialogComponent, MessageDialogData, MessageType } from '../../iqb-common'; // import { AboutComponent } from './../../about'; -import { MainDatastoreService } from './../../admin/maindatastore.service'; import { BackendService as BackendServiceReadOnly } from './../../backend.service'; import { BackendService as BackendServiceSuperAdmin } from './../backend.service'; // import * as Quill from 'quill'; @@ -26,7 +25,6 @@ export class AboutTextComponent implements OnInit { aboutText: string; constructor( - private mds: MainDatastoreService, private bsRO: BackendServiceReadOnly, private bsSA: BackendServiceSuperAdmin, private fb: FormBuilder, @@ -37,13 +35,13 @@ export class AboutTextComponent implements OnInit { this.aboutTextForm = this.fb.group({ myTextArea: this.fb.control('') }); - this.mds.pageTitle$.next(''); - this.bsRO.getAboutText().subscribe(t => this.aboutTextForm.get('myTextArea').setValue(t as string)); + // this.mds.pageTitle$.next(''); + // this.bsRO.getAboutText().subscribe(t => this.aboutTextForm.get('myTextArea').setValue(t as string)); } setAboutText() { - this.bsSA.setAboutText(this.mds.adminToken$.getValue(), this.aboutTextForm.get('myTextArea').value).subscribe(respOk => { + this.bsSA.setAboutText(this.aboutTextForm.get('myTextArea').value).subscribe(respOk => { if (respOk) { this.snackBar.open('Geänderter Text', '', {duration: 1500}); } else { diff --git a/src/app/superadmin/backend.service.ts b/src/app/superadmin/backend.service.ts index b51b73639032181a78a330ed1589601eb4a3136d..92aa7a93eb1e69e948281a7d6f50be83518540cd 100644 --- a/src/app/superadmin/backend.service.ts +++ b/src/app/superadmin/backend.service.ts @@ -20,177 +20,177 @@ export class BackendService { } // ******************************************************************* - getUsers(token: string): Observable<GetUserDataResponse[] | ServerError> { + getUsers(): Observable<GetUserDataResponse[] | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<GetUserDataResponse[]>(this.serverUrl + 'getUsers.php', {t: token}, httpOptions) + .post<GetUserDataResponse[]>(this.serverUrl + 'getUsers.php', {}, httpOptions) .pipe( catchError(this.handleError) ); } - addUser(token: string, name: string, password: string): Observable<Boolean | ServerError> { + addUser(name: string, password: string): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'addUser.php', {t: token, n: name, p: password}, httpOptions) + .post<Boolean>(this.serverUrl + 'addUser.php', {n: name, p: password}, httpOptions) .pipe( catchError(this.handleError) ); } - changePassword(token: string, name: string, password: string): Observable<Boolean | ServerError> { + changePassword(name: string, password: string): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'setPassword.php', {t: token, n: name, p: password}, httpOptions) + .post<Boolean>(this.serverUrl + 'setPassword.php', {n: name, p: password}, httpOptions) .pipe( catchError(this.handleError) ); } - deleteUsers(token: string, users: string[]): Observable<Boolean | ServerError> { + deleteUsers(users: string[]): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'deleteUsers.php', {t: token, u: users}, httpOptions) + .post<Boolean>(this.serverUrl + 'deleteUsers.php', {u: users}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - getWorkspacesByUser(token: string, username: string): Observable<IdLabelSelectedData[] | ServerError> { + getWorkspacesByUser(username: string): Observable<IdLabelSelectedData[] | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<IdLabelSelectedData[]>(this.serverUrl + 'getUserWorkspaces.php', {t: token, u: username}, httpOptions) + .post<IdLabelSelectedData[]>(this.serverUrl + 'getUserWorkspaces.php', {u: username}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - setWorkspacesByUser(token: string, user: string, accessTo: IdLabelSelectedData[]): Observable<Boolean | ServerError> { + setWorkspacesByUser(user: string, accessTo: IdLabelSelectedData[]): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'setUserWorkspaces.php', {t: token, u: user, w: accessTo}, httpOptions) + .post<Boolean>(this.serverUrl + 'setUserWorkspaces.php', {u: user, w: accessTo}, httpOptions) .pipe( catchError(this.handleError) ); } - setAboutText(token: string, text: string): Observable<string | ServerError> { + setAboutText(text: string): Observable<string | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/text' }) }; return this.http - .post<string>(this.serverUrl + 'setAboutText.php', {t: token, text: text}, httpOptions).pipe(catchError(this.handleError)); + .post<string>(this.serverUrl + 'setAboutText.php', {text: text}, httpOptions).pipe(catchError(this.handleError)); } // ******************************************************************* // ******************************************************************* - addWorkspace(token: string, name: string): Observable<Boolean | ServerError> { + addWorkspace(name: string): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'addWorkspace.php', {t: token, n: name}, httpOptions) + .post<Boolean>(this.serverUrl + 'addWorkspace.php', {n: name}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - changeWorkspace(token: string, wsId: number, wsName: string): Observable<Boolean | ServerError> { + changeWorkspace(wsId: number, wsName: string): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'setWorkspace.php', {t: token, ws_id: wsId, ws_name: wsName}, httpOptions) + .post<Boolean>(this.serverUrl + 'setWorkspace.php', {ws_id: wsId, ws_name: wsName}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - deleteWorkspaces(token: string, workspaces: number[]): Observable<Boolean | ServerError> { + deleteWorkspaces(workspaces: number[]): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'deleteWorkspaces.php', {t: token, ws: workspaces}, httpOptions) + .post<Boolean>(this.serverUrl + 'deleteWorkspaces.php', {ws: workspaces}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - getUsersByWorkspace(token: string, workspaceId: number): Observable<IdLabelSelectedData[] | ServerError> { + getUsersByWorkspace(workspaceId: number): Observable<IdLabelSelectedData[] | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<IdLabelSelectedData[]>(this.serverUrl + 'getWorkspaceUsers.php', {t: token, ws: workspaceId}, httpOptions) + .post<IdLabelSelectedData[]>(this.serverUrl + 'getWorkspaceUsers.php', {ws: workspaceId}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - setUsersByWorkspace(token: string, workspace: number, accessing: IdLabelSelectedData[]): Observable<Boolean | ServerError> { + setUsersByWorkspace(workspace: number, accessing: IdLabelSelectedData[]): Observable<Boolean | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<Boolean>(this.serverUrl + 'setWorkspaceUsers.php', {t: token, w: workspace, u: accessing}, httpOptions) + .post<Boolean>(this.serverUrl + 'setWorkspaceUsers.php', {w: workspace, u: accessing}, httpOptions) .pipe( catchError(this.handleError) ); } // ******************************************************************* - getWorkspaces(token: string): Observable<IdLabelSelectedData[] | ServerError> { + getWorkspaces(): Observable<IdLabelSelectedData[] | ServerError> { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this.http - .post<IdLabelSelectedData[]>(this.serverUrl + 'getWorkspaces.php', {t: token}, httpOptions) + .post<IdLabelSelectedData[]>(this.serverUrl + 'getWorkspaces.php', {}, httpOptions) .pipe( catchError(this.handleError) ); diff --git a/src/app/superadmin/dashboard/dashboard.component.ts b/src/app/superadmin/dashboard/dashboard.component.ts index f23ddb0cbfae7b674c4d26878c48f55de8e9bc62..3f95657b582f30e702b1e8a31e200380aba4a22d 100644 --- a/src/app/superadmin/dashboard/dashboard.component.ts +++ b/src/app/superadmin/dashboard/dashboard.component.ts @@ -1,4 +1,3 @@ -import { MainDatastoreService } from './../../admin/maindatastore.service'; import { Component, OnInit } from '@angular/core'; @@ -10,9 +9,7 @@ import { Component, OnInit } from '@angular/core'; export class DashboardComponent implements OnInit { public isSuperadmin = false; - constructor(private mds: MainDatastoreService) { - this.mds.isSuperadmin$.subscribe( - is => this.isSuperadmin = is); + constructor() { } ngOnInit() { diff --git a/src/app/superadmin/superadmin.component.ts b/src/app/superadmin/superadmin.component.ts index dd22b5386f9a3bf6d8089f31a2f3cffb322c06d4..4a5af8bc84b02763d189809cad8f9809eda0347f 100644 --- a/src/app/superadmin/superadmin.component.ts +++ b/src/app/superadmin/superadmin.component.ts @@ -1,4 +1,3 @@ -import { MainDatastoreService } from './../admin/maindatastore.service'; import { Component, OnInit } from '@angular/core'; @@ -18,11 +17,10 @@ export class SuperadminComponent implements OnInit { constructor( - private mds: MainDatastoreService ) { } ngOnInit() { - this.mds.updatePageTitle('IQB-Testcenter Verwaltung: Nutzer und Arbeitsbereiche'); + // this.mds.updatePageTitle('IQB-Testcenter Verwaltung: Nutzer und Arbeitsbereiche'); } } diff --git a/src/app/superadmin/users/users.component.ts b/src/app/superadmin/users/users.component.ts index 4e1ee6ccec49c26cd5eeefbfd8807b7e415cce40..2694d5bb99ca634b57fa852011cdddc5ba5e0a95 100644 --- a/src/app/superadmin/users/users.component.ts +++ b/src/app/superadmin/users/users.component.ts @@ -1,4 +1,3 @@ -import { MainDatastoreService } from './../../admin/maindatastore.service'; import { NewpasswordComponent } from './newpassword/newpassword.component'; import { NewuserComponent } from './newuser/newuser.component'; import { BackendService, GetUserDataResponse, IdLabelSelectedData, ServerError } from '../backend.service'; @@ -33,7 +32,6 @@ export class UsersComponent implements OnInit { @ViewChild(MatSort) sort: MatSort; constructor( - private mds: MainDatastoreService, private bs: BackendService, private newuserDialog: MatDialog, private newpasswordDialog: MatDialog, @@ -49,10 +47,7 @@ export class UsersComponent implements OnInit { } ngOnInit() { - this.mds.isSuperadmin$.subscribe(i => { - this.isSuperadmin = i; - this.updateObjectList(); - }); + this.updateObjectList(); } // *********************************************************************************** @@ -67,9 +62,7 @@ export class UsersComponent implements OnInit { dialogRef.afterClosed().subscribe(result => { if (typeof result !== 'undefined') { if (result !== false) { - this.bs.addUser( - this.mds.adminToken$.getValue(), - (<FormGroup>result).get('name').value, + this.bs.addUser((<FormGroup>result).get('name').value, (<FormGroup>result).get('pw').value).subscribe( respOk => { if (respOk) { @@ -110,9 +103,7 @@ export class UsersComponent implements OnInit { if (typeof result !== 'undefined') { if (result !== false) { this.dataLoading = true; - this.bs.changePassword( - this.mds.adminToken$.getValue(), - selectedRows[0]['name'], + this.bs.changePassword(selectedRows[0]['name'], (<FormGroup>result).get('pw').value).subscribe( respOk => { if (respOk) { @@ -164,7 +155,7 @@ export class UsersComponent implements OnInit { this.dataLoading = true; const usersToDelete = []; selectedRows.forEach((r: GetUserDataResponse) => usersToDelete.push(r.name)); - this.bs.deleteUsers(this.mds.adminToken$.getValue(), usersToDelete).subscribe( + this.bs.deleteUsers(usersToDelete).subscribe( respOk => { if (respOk) { this.snackBar.open('Nutzer gelöscht', '', {duration: 1000}); @@ -185,7 +176,7 @@ export class UsersComponent implements OnInit { this.pendingWorkspaceChanges = false; if (this.selectedUser.length > 0) { this.dataLoading = true; - this.bs.getWorkspacesByUser(this.mds.adminToken$.getValue(), this.selectedUser).subscribe( + this.bs.getWorkspacesByUser(this.selectedUser).subscribe( (dataresponse: IdLabelSelectedData[]) => { this.WorkspacelistDatasource = new MatTableDataSource(dataresponse); this.dataLoading = false; @@ -207,7 +198,7 @@ export class UsersComponent implements OnInit { this.pendingWorkspaceChanges = false; if (this.selectedUser.length > 0) { this.dataLoading = true; - this.bs.setWorkspacesByUser(this.mds.adminToken$.getValue(), this.selectedUser, this.WorkspacelistDatasource.data).subscribe( + this.bs.setWorkspacesByUser(this.selectedUser, this.WorkspacelistDatasource.data).subscribe( respOk => { if (respOk) { this.snackBar.open('Zugriffsrechte geändert', '', {duration: 1000}); @@ -225,7 +216,7 @@ export class UsersComponent implements OnInit { updateObjectList() { if (this.isSuperadmin) { this.dataLoading = true; - this.bs.getUsers(this.mds.adminToken$.getValue()).subscribe( + this.bs.getUsers().subscribe( (dataresponse: GetUserDataResponse[]) => { this.objectsDatasource = new MatTableDataSource(dataresponse); this.objectsDatasource.sort = this.sort; diff --git a/src/app/superadmin/workspaces/workspaces.component.ts b/src/app/superadmin/workspaces/workspaces.component.ts index c949986e57a00ee867b046417702c3c34c7dfd1f..f8f45976756df0b1524c49fc9af9817ca7ce8a09 100644 --- a/src/app/superadmin/workspaces/workspaces.component.ts +++ b/src/app/superadmin/workspaces/workspaces.component.ts @@ -1,4 +1,3 @@ -import { MainDatastoreService } from './../../admin/maindatastore.service'; import { EditworkspaceComponent } from './editworkspace/editworkspace.component'; import { NewworkspaceComponent } from './newworkspace/newworkspace.component'; import { BackendService, GetUserDataResponse, IdLabelSelectedData, ServerError } from '../backend.service'; @@ -34,7 +33,6 @@ export class WorkspacesComponent implements OnInit { @ViewChild(MatSort) sort: MatSort; constructor( - private mds: MainDatastoreService, private bs: BackendService, private newworkspaceDialog: MatDialog, private editworkspaceDialog: MatDialog, @@ -56,10 +54,7 @@ export class WorkspacesComponent implements OnInit { } ngOnInit() { - this.mds.isSuperadmin$.subscribe(i => { - this.isSuperadmin = i; - this.updateObjectList(); - }); + this.updateObjectList(); } // *********************************************************************************** @@ -75,9 +70,7 @@ export class WorkspacesComponent implements OnInit { if (typeof result !== 'undefined') { if (result !== false) { this.dataLoading = true; - this.bs.addWorkspace( - this.mds.adminToken$.getValue(), - (<FormGroup>result).get('name').value).subscribe( + this.bs.addWorkspace((<FormGroup>result).get('name').value).subscribe( respOk => { if (respOk) { this.snackBar.open('Arbeitsbereich hinzugefügt', '', {duration: 1000}); @@ -119,9 +112,7 @@ export class WorkspacesComponent implements OnInit { if (typeof result !== 'undefined') { if (result !== false) { this.dataLoading = true; - this.bs.changeWorkspace( - this.mds.adminToken$.getValue(), - selectedRows[0].id, + this.bs.changeWorkspace(selectedRows[0].id, (<FormGroup>result).get('name').value).subscribe( respOk => { if (respOk) { @@ -174,7 +165,7 @@ export class WorkspacesComponent implements OnInit { this.dataLoading = true; const workspacesToDelete = []; selectedRows.forEach((r: IdLabelSelectedData) => workspacesToDelete.push(r.id)); - this.bs.deleteWorkspaces(this.mds.adminToken$.getValue(), workspacesToDelete).subscribe( + this.bs.deleteWorkspaces(workspacesToDelete).subscribe( respOk => { if (respOk) { this.snackBar.open('Arbeitsbereich/e gelöscht', '', {duration: 1000}); @@ -195,7 +186,7 @@ export class WorkspacesComponent implements OnInit { this.pendingUserChanges = false; if (this.selectedWorkspaceId > 0) { this.dataLoading = true; - this.bs.getUsersByWorkspace(this.mds.adminToken$.getValue(), this.selectedWorkspaceId).subscribe( + this.bs.getUsersByWorkspace(this.selectedWorkspaceId).subscribe( (dataresponse: IdLabelSelectedData[]) => { this.UserlistDatasource = new MatTableDataSource(dataresponse); this.dataLoading = false; @@ -217,7 +208,7 @@ export class WorkspacesComponent implements OnInit { this.pendingUserChanges = false; if (this.selectedWorkspaceId > 0) { this.dataLoading = true; - this.bs.setUsersByWorkspace(this.mds.adminToken$.getValue(), this.selectedWorkspaceId, this.UserlistDatasource.data).subscribe( + this.bs.setUsersByWorkspace(this.selectedWorkspaceId, this.UserlistDatasource.data).subscribe( respOk => { if (respOk) { this.snackBar.open('Zugriffsrechte geändert', '', {duration: 1000}); @@ -235,7 +226,7 @@ export class WorkspacesComponent implements OnInit { updateObjectList() { if (this.isSuperadmin) { this.dataLoading = true; - this.bs.getWorkspaces(this.mds.adminToken$.getValue()).subscribe( + this.bs.getWorkspaces().subscribe( (dataresponse: IdLabelSelectedData[]) => { this.objectsDatasource = new MatTableDataSource(dataresponse); this.objectsDatasource.sort = this.sort; diff --git a/src/app/admin/backend.service.spec.ts b/src/app/workspace/backend.service.spec.ts similarity index 100% rename from src/app/admin/backend.service.spec.ts rename to src/app/workspace/backend.service.spec.ts diff --git a/src/app/workspace/backend.service.ts b/src/app/workspace/backend.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..e56ca36f4dfc0fe00493e1df5d4131a4df4ff5c2 --- /dev/null +++ b/src/app/workspace/backend.service.ts @@ -0,0 +1,232 @@ +import { GetFileResponseData, CheckWorkspaceResponseData, BookletsStarted, SysCheckStatistics, ReviewData, LogData, UnitResponse, ResultData, MonitorData } from './workspace.interfaces'; +import { Injectable, Inject } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpErrorResponse, HttpUrlEncodingCodec } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +// import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { catchError } from 'rxjs/operators'; +import { ErrorHandler, ServerError } from '../backend.service'; + +@Injectable() +export class BackendService { + private serverUrlSlim = ''; + + constructor( + @Inject('SERVER_URL') private serverUrl: string, + private http: HttpClient) { + this.serverUrlSlim = this.serverUrl + 'php/ws.php/' + this.serverUrl = this.serverUrl + 'php_admin/'; + } + + + // ******************************************************************* + getFiles(): Observable<GetFileResponseData[] | ServerError> { + return this.http + .get<GetFileResponseData[]>(this.serverUrlSlim + 'filelist') + .pipe( + catchError(ErrorHandler.handle) + ); + } + + // ******************************************************************* + // Fehlerbehandlung beim Aufrufer + deleteFiles(filesToDelete: Array<string>): Observable<string | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<string>(this.serverUrl + 'deleteFiles.php', {f: filesToDelete}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + // ******************************************************************* + checkWorkspace(): Observable<CheckWorkspaceResponseData | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<CheckWorkspaceResponseData>(this.serverUrl + 'checkWorkspace.php', {}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + getBookletsStarted(groups: string[]): Observable<BookletsStarted[] | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<BookletsStarted[]>(this.serverUrl + 'getBookletsStarted.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + lockBooklets(groups: string[]): Observable<boolean | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<boolean>(this.serverUrl + 'lockBooklets.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + unlockBooklets(groups: string[]): Observable<boolean | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<boolean>(this.serverUrl + 'unlockBooklets.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); +} + + /*******************************/ + getMonitorData(): Observable<MonitorData[] | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<MonitorData[]>(this.serverUrl + 'getMonitorData.php', {}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); +} + + /*******************************/ + getResultData(): Observable<ResultData[] | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<ResultData[]>(this.serverUrl + 'getResultData.php', {}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + getResponses(groups: string[]): Observable<UnitResponse[] | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<UnitResponse[]>(this.serverUrl + 'getResponses.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); +} + + /*******************************/ + getLogs(groups: string[]): Observable<LogData[] | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<LogData[]>(this.serverUrl + 'getLogs.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + getReviews(groups: string[]): Observable<ReviewData[] | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<ReviewData[]>(this.serverUrl + 'getReviews.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + deleteData(groups: string[]): Observable<boolean | ServerError>{ + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<boolean>(this.serverUrl + 'deleteData.php', {g: groups}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + getSysCheckReportList(): Observable<SysCheckStatistics[] | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<SysCheckStatistics[]>(this.serverUrl + 'getSysCheckReportList.php', {}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + getSysCheckReport(reports: string[], columnDelimiter: string, quoteChar: string): Observable<string[] | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<string[]>(this.serverUrl + 'getSysCheckReport.php', + {r: reports, cd: columnDelimiter, q: quoteChar}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } + + /*******************************/ + deleteSysCheckReports(reports: string[]): Observable<boolean | ServerError> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<boolean>(this.serverUrl + 'deleteSysCheckReports.php', + {r: reports}, httpOptions) + .pipe( + catchError(ErrorHandler.handle) + ); + } +} + + +// ############################################################################################# + diff --git a/src/app/admin/myfiles/myfiles.component.css b/src/app/workspace/files/files.component.css similarity index 100% rename from src/app/admin/myfiles/myfiles.component.css rename to src/app/workspace/files/files.component.css diff --git a/src/app/admin/myfiles/myfiles.component.html b/src/app/workspace/files/files.component.html similarity index 96% rename from src/app/admin/myfiles/myfiles.component.html rename to src/app/workspace/files/files.component.html index 36056c9ebf4176c643d8a2240fe6e543f433140a..a4989ec2067865a937953f9de2931b0ebefda717 100644 --- a/src/app/admin/myfiles/myfiles.component.html +++ b/src/app/workspace/files/files.component.html @@ -5,7 +5,7 @@ <!-- ============================================= --> <div class="filelist"> - <mat-table #table *ngIf="isAdmin" [dataSource]="serverfiles" matSort> + <mat-table #table [dataSource]="serverfiles" matSort> <ng-container matColumnDef="checked"> <mat-header-cell *matHeaderCellDef class="checkboxcell"> <mat-checkbox (change)="checkAll($event.checked)"></mat-checkbox> @@ -42,7 +42,7 @@ </div> <!-- ============================================= --> - <div class="uploads" *ngIf="isAdmin"> + <div class="uploads"> <button mat-raised-button (click)="deleteFiles()" matTooltip="Markierte Dateien löschen" matTooltipPosition="above"> <mat-icon>delete</mat-icon> </button> diff --git a/src/app/admin/admin.component.spec.ts b/src/app/workspace/files/files.component.spec.ts similarity index 57% rename from src/app/admin/admin.component.spec.ts rename to src/app/workspace/files/files.component.spec.ts index 72e742ff122ab761481f1642aa72e77385cddf8d..da941cc1423eb4e0ccee04599f7a30827265304c 100644 --- a/src/app/admin/admin.component.spec.ts +++ b/src/app/workspace/files/files.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdminComponent } from './admin.component'; +import { FilesComponent } from './files.component'; -describe('AdminComponent', () => { - let component: AdminComponent; - let fixture: ComponentFixture<AdminComponent>; +describe('FilesComponent', () => { + let component: FilesComponent; + let fixture: ComponentFixture<FilesComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ AdminComponent ] + declarations: [ FilesComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(AdminComponent); + fixture = TestBed.createComponent(FilesComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/admin/myfiles/myfiles.component.ts b/src/app/workspace/files/files.component.ts similarity index 52% rename from src/app/admin/myfiles/myfiles.component.ts rename to src/app/workspace/files/files.component.ts index 56a7dfba04c5254e7dfd824e99fe7b58d7ac0cc5..e9350259b134ae92d17b83d22bcd5ef03f76b2ee 100644 --- a/src/app/admin/myfiles/myfiles.component.ts +++ b/src/app/workspace/files/files.component.ts @@ -1,12 +1,16 @@ -import { MainDatastoreService } from './../maindatastore.service'; +import { LoginData } from './../../app.interfaces'; +import { MainDataService } from './../../maindata.service'; +import { ServerError } from './../../backend.service'; +import { WorkspaceDataService } from './../workspacedata.service'; +import { GetFileResponseData, CheckWorkspaceResponseData } from './../workspace.interfaces'; import { ConfirmDialogComponent, ConfirmDialogData, MessageDialogComponent, MessageDialogData, MessageType } from '../../iqb-common'; import { DataSource } from '@angular/cdk/collections'; -import { Observable, BehaviorSubject } from 'rxjs'; +import { Observable, BehaviorSubject, Subscription, merge } from 'rxjs'; import { MatTableDataSource } from '@angular/material/table'; import { MatSnackBar } from '@angular/material'; -import { BackendService, GetFileResponseData, CheckWorkspaceResponseData, ServerError } from '../backend.service'; -import { Input, Output, EventEmitter, Component, OnInit, Inject, ElementRef } from '@angular/core'; +import { BackendService } from '../backend.service'; +import { Input, Output, EventEmitter, Component, OnInit, Inject, ElementRef, OnDestroy } from '@angular/core'; import { NgModule, ViewChild } from '@angular/core'; import { MatSort, MatDialog } from '@angular/material'; import { HttpEventType, HttpErrorResponse, HttpEvent } from '@angular/common/http'; @@ -14,20 +18,16 @@ import { IqbFilesUploadQueueComponent, IqbFilesUploadInputForDirective } from '. @Component({ - templateUrl: './myfiles.component.html', - styleUrls: ['./myfiles.component.css'] + templateUrl: './files.component.html', + styleUrls: ['./files.component.css'] }) -export class MyfilesComponent implements OnInit { +export class FilesComponent implements OnInit, OnDestroy { public serverfiles: MatTableDataSource<GetFileResponseData>; public displayedColumns = ['checked', 'filename', 'typelabel', 'filesize', 'filedatetime']; public uploadUrl = ''; public fileNameAlias = 'fileforvo'; public dataLoading = false; - - // for iqb-FileUpload - private isAdmin = false; - public token = ''; - public workspace = -1; + private logindataSubscription: Subscription = null; // for workspace-check public checkErrors = []; @@ -39,23 +39,21 @@ export class MyfilesComponent implements OnInit { constructor( @Inject('SERVER_URL') private serverUrl: string, private bs: BackendService, - private mds: MainDatastoreService, + private mds: MainDataService, + private wds: WorkspaceDataService, public confirmDialog: MatDialog, public messsageDialog: MatDialog, public snackBar: MatSnackBar ) { - this.mds.isAdmin$.subscribe(i => { - this.isAdmin = i; - }); - this.uploadUrl = this.serverUrl + 'admin/php_admin/uploadFile.php'; + this.uploadUrl = this.serverUrl + 'php_admin/uploadFile.php'; } ngOnInit() { - this.mds.workspaceId$.subscribe(ws => { - this.updateFileList(); - this.workspace = ws; + this.logindataSubscription = this.mds.loginData$.subscribe(ld => { + const ws = this.wds.ws; + let at = ld ? ld.admintoken : ''; + this.updateFileList((ws <= 0) || (at.length === 0)); }); - this.mds.adminToken$.subscribe(token => this.token = token); } // *********************************************************************************** @@ -98,17 +96,20 @@ export class MyfilesComponent implements OnInit { if (result !== false) { // ========================================================= this.dataLoading = true; - this.bs.deleteFiles(this.mds.adminToken$.getValue(), this.mds.workspaceId$.getValue(), filesToDelete).subscribe( - (deletefilesresponse: string) => { - if ((deletefilesresponse.length > 5) && (deletefilesresponse.substr(0, 2) === 'e:')) { - this.snackBar.open(deletefilesresponse.substr(2), 'Fehler', {duration: 1000}); + this.bs.deleteFiles(filesToDelete).subscribe(deletefilesresponse => { + if (deletefilesresponse instanceof ServerError) { + this.wds.setNewErrorMsg(deletefilesresponse as ServerError); + } else { + const deletefilesresponseOk = deletefilesresponse as string; + if ((deletefilesresponseOk.length > 5) && (deletefilesresponseOk.substr(0, 2) === 'e:')) { + this.snackBar.open(deletefilesresponseOk.substr(2), 'Fehler', {duration: 1000}); } else { - this.snackBar.open(deletefilesresponse, '', {duration: 1000}); + this.snackBar.open(deletefilesresponseOk, '', {duration: 1000}); this.updateFileList(); } - }, (err: ServerError) => { - this.mds.updateAdminStatus('', '', [], false, err.label); - }); + this.wds.setNewErrorMsg(); + } + }); // ========================================================= } }); @@ -126,42 +127,36 @@ export class MyfilesComponent implements OnInit { } // *********************************************************************************** - updateFileList() { + updateFileList(empty = false) { this.checkErrors = []; this.checkWarnings = []; this.checkInfos = []; - if (this.isAdmin) { - const myWorkspaceId = this.mds.workspaceId$.getValue(); - if (myWorkspaceId < 0) { - this.serverfiles = null; - this.dataLoading = false; - } else { - this.dataLoading = true; - this.bs.getFiles(this.mds.adminToken$.getValue(), myWorkspaceId).subscribe( - (filedataresponse: GetFileResponseData[]) => { - this.serverfiles = new MatTableDataSource(filedataresponse); - this.serverfiles.sort = this.sort; - this.dataLoading = false; - }, (err: ServerError) => { - this.mds.updateAdminStatus('', '', [], false, err.label); - this.dataLoading = false; - } - ); - } + if (empty) { + this.serverfiles = new MatTableDataSource([]); } else { - this.serverfiles = null; - this.dataLoading = false; + this.dataLoading = true; + this.bs.getFiles().subscribe( + (filedataresponse: GetFileResponseData[]) => { + this.serverfiles = new MatTableDataSource(filedataresponse); + this.serverfiles.sort = this.sort; + this.dataLoading = false; + this.wds.setNewErrorMsg(); + }, (err: ServerError) => { + this.wds.setNewErrorMsg(err); + this.dataLoading = false; + } + ); } } // *********************************************************************************** getDownloadRef(element: GetFileResponseData): string { return this.serverUrl - + 'admin/php_admin/getFile.php?at=' + this.mds.adminToken$.getValue() - + '&ws=' + this.mds.workspaceId$.getValue() - + '&t=' + element.type - + '&fn=' + element.filename; + + 'php/getFile.php?t=' + element.type + + '&fn=' + element.filename + + '&at=' + this.mds.adminToken + + '&ws=' + this.wds.ws.toString(); } checkWorkspace() { @@ -169,31 +164,28 @@ export class MyfilesComponent implements OnInit { this.checkWarnings = []; this.checkInfos = []; - if (this.isAdmin) { - const myWorkspaceId = this.mds.workspaceId$.getValue(); - if (myWorkspaceId < 0) { - // this.serverfiles = null; + this.dataLoading = true; + this.bs.checkWorkspace().subscribe( + (checkResponse: CheckWorkspaceResponseData) => { + // this.serverfiles = new MatTableDataSource(filedataresponse); + // this.serverfiles.sort = this.sort; + this.checkErrors = checkResponse.errors; + this.checkWarnings = checkResponse.warnings; + this.checkInfos = checkResponse.infos; + this.wds.setNewErrorMsg(); + + this.dataLoading = false; + }, (err: ServerError) => { + this.wds.setNewErrorMsg(err); this.dataLoading = false; - } else { - this.dataLoading = true; - this.bs.checkWorkspace(this.mds.adminToken$.getValue(), myWorkspaceId).subscribe( - (checkResponse: CheckWorkspaceResponseData) => { - // this.serverfiles = new MatTableDataSource(filedataresponse); - // this.serverfiles.sort = this.sort; - this.checkErrors = checkResponse.errors; - this.checkWarnings = checkResponse.warnings; - this.checkInfos = checkResponse.infos; - - this.dataLoading = false; - }, (err: ServerError) => { - this.mds.updateAdminStatus('', '', [], false, err.label); - this.dataLoading = false; - } - ); } - } else { - // this.serverfiles = null; - this.dataLoading = false; + ); + } + + // % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + ngOnDestroy() { + if (this.logindataSubscription !== null) { + this.logindataSubscription.unsubscribe(); } } } diff --git a/src/app/workspace/index.ts b/src/app/workspace/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..30c7aa4c2daa6ada54bf85e7876130ae495ef6a5 --- /dev/null +++ b/src/app/workspace/index.ts @@ -0,0 +1,3 @@ +export { WorkspaceComponent } from './workspace.component'; +export { WorkspaceModule } from './workspace.module'; +export { WorkspaceDataService } from './workspacedata.service'; diff --git a/src/app/admin/monitor/monitor.component.css b/src/app/workspace/monitor/monitor.component.css similarity index 100% rename from src/app/admin/monitor/monitor.component.css rename to src/app/workspace/monitor/monitor.component.css diff --git a/src/app/admin/monitor/monitor.component.html b/src/app/workspace/monitor/monitor.component.html similarity index 100% rename from src/app/admin/monitor/monitor.component.html rename to src/app/workspace/monitor/monitor.component.html diff --git a/src/app/admin/monitor/monitor.component.spec.ts b/src/app/workspace/monitor/monitor.component.spec.ts similarity index 100% rename from src/app/admin/monitor/monitor.component.spec.ts rename to src/app/workspace/monitor/monitor.component.spec.ts diff --git a/src/app/admin/monitor/monitor.component.ts b/src/app/workspace/monitor/monitor.component.ts similarity index 80% rename from src/app/admin/monitor/monitor.component.ts rename to src/app/workspace/monitor/monitor.component.ts index ef4630b3808344371c7aae5f7ca6b518db4e8c48..73cd8731e7e29d03113717ef7c54c6a4c94eb715 100644 --- a/src/app/admin/monitor/monitor.component.ts +++ b/src/app/workspace/monitor/monitor.component.ts @@ -1,9 +1,11 @@ -import { BackendService, MonitorData, BookletsStarted } from './../backend.service'; -import { MainDatastoreService } from './../maindatastore.service'; +import { BookletsStarted } from './../workspace.interfaces'; +import { WorkspaceDataService } from './../workspacedata.service'; +import { BackendService } from './../backend.service'; import { Component, OnInit, ViewChild } from '@angular/core'; import { MatSnackBar, MatSort, MatTableDataSource } from '@angular/material'; import { SelectionModel } from '@angular/cdk/collections'; import { saveAs } from 'file-saver'; +import { MonitorData } from '../workspace.interfaces'; @Component({ @@ -15,29 +17,24 @@ export class MonitorComponent implements OnInit { displayedColumns: string[] = ['selectCheckbox', 'groupname', 'loginsPrepared', 'personsPrepared', 'bookletsPrepared', 'bookletsStarted', 'bookletsLocked']; private monitorDataSource = new MatTableDataSource<MonitorData>([]); - private isAdmin = false; private tableselectionCheckbox = new SelectionModel<MonitorData>(true, []); private dataLoading = false; @ViewChild(MatSort) sort: MatSort; constructor( private bs: BackendService, - private mds: MainDatastoreService, + private wds: WorkspaceDataService, public snackBar: MatSnackBar - ) { - this.mds.isAdmin$.subscribe( - i => this.isAdmin = i); - } + ) { } ngOnInit() { - this.mds.adminToken$.subscribe(at => this.updateTable()); - this.mds.workspaceId$.subscribe(ws => this.updateTable()); + this.updateTable(); } updateTable() { this.dataLoading = true; this.tableselectionCheckbox.clear; - this.bs.getMonitorData(this.mds.adminToken$.getValue(), this.mds.workspaceId$.getValue()).subscribe( + this.bs.getMonitorData().subscribe( (monitorData: MonitorData[]) => { this.dataLoading = false; this.monitorDataSource = new MatTableDataSource<MonitorData>(monitorData); @@ -66,10 +63,7 @@ export class MonitorComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedGroups.push(element.groupname); }); - this.bs.getBookletsStarted( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe(bData => { + this.bs.getBookletsStarted(selectedGroups).subscribe(bData => { const bookletList = bData as BookletsStarted[]; if (bookletList.length > 0) { @@ -102,10 +96,7 @@ export class MonitorComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedGroups.push(element.groupname); }); - this.bs.lockBooklets( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe(success => { + this.bs.lockBooklets(selectedGroups).subscribe(success => { const ok = success as boolean; if (ok) { this.snackBar.open('Testhefte wurden gesperrt.', 'Sperrung', {duration: 1000}); @@ -125,10 +116,7 @@ export class MonitorComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedGroups.push(element.groupname); }); - this.bs.unlockBooklets( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe(success => { + this.bs.unlockBooklets(selectedGroups).subscribe(success => { const ok = success as boolean; if (ok) { this.snackBar.open('Testhefte wurden freigegeben.', 'Sperrung', {duration: 1000}); diff --git a/src/app/admin/results/results.component.css b/src/app/workspace/results/results.component.css similarity index 100% rename from src/app/admin/results/results.component.css rename to src/app/workspace/results/results.component.css diff --git a/src/app/admin/results/results.component.html b/src/app/workspace/results/results.component.html similarity index 100% rename from src/app/admin/results/results.component.html rename to src/app/workspace/results/results.component.html diff --git a/src/app/admin/results/results.component.spec.ts b/src/app/workspace/results/results.component.spec.ts similarity index 100% rename from src/app/admin/results/results.component.spec.ts rename to src/app/workspace/results/results.component.spec.ts diff --git a/src/app/admin/results/results.component.ts b/src/app/workspace/results/results.component.ts similarity index 89% rename from src/app/admin/results/results.component.ts rename to src/app/workspace/results/results.component.ts index dff4741451a70bda473ba408ea60fd8a73addc19..e813e9188b26b69f1219836620014eaa8089eb16 100644 --- a/src/app/admin/results/results.component.ts +++ b/src/app/workspace/results/results.component.ts @@ -1,10 +1,12 @@ +import { LogData } from './../workspace.interfaces'; +import { WorkspaceDataService } from './../workspacedata.service'; import { ConfirmDialogComponent, ConfirmDialogData } from './../../iqb-common/confirm-dialog/confirm-dialog.component'; import { Component, OnInit, ViewChild } from '@angular/core'; -import { BackendService, UnitResponse, ResultData, ReviewData, LogData } from './../backend.service'; -import { MainDatastoreService } from './../maindatastore.service'; +import { BackendService } from './../backend.service'; import { MatSnackBar, MatSort, MatTableDataSource, MatDialog } from '@angular/material'; import { SelectionModel } from '@angular/cdk/collections'; import { saveAs } from 'file-saver'; +import { ResultData, UnitResponse, ReviewData } from '../workspace.interfaces'; @Component({ @@ -14,7 +16,6 @@ import { saveAs } from 'file-saver'; export class ResultsComponent implements OnInit { displayedColumns: string[] = ['selectCheckbox', 'groupname', 'bookletsStarted', 'num_units_min', 'num_units_max', 'num_units_mean']; private resultDataSource = new MatTableDataSource<ResultData>([]); - private isAdmin = false; // prepared for selection if needed sometime private tableselectionCheckbox = new SelectionModel<ResultData>(true, []); private dataLoading = false; @@ -23,24 +24,19 @@ export class ResultsComponent implements OnInit { constructor( private bs: BackendService, - private mds: MainDatastoreService, + private wds: WorkspaceDataService, private deleteConfirmDialog: MatDialog, public snackBar: MatSnackBar - ) { - this.mds.isAdmin$.subscribe( - i => this.isAdmin = i); - } + ) { } ngOnInit() { - this.mds.adminToken$.subscribe(at => this.updateTable()); - this.mds.workspaceId$.subscribe(ws => this.updateTable()); - // console.log(saveAs); + this.updateTable(); } updateTable() { this.dataLoading = true; this.tableselectionCheckbox.clear(); - this.bs.getResultData(this.mds.adminToken$.getValue(), this.mds.workspaceId$.getValue()).subscribe( + this.bs.getResultData().subscribe( (resultData: ResultData[]) => { this.dataLoading = false; this.resultDataSource = new MatTableDataSource<ResultData>(resultData); @@ -69,10 +65,7 @@ export class ResultsComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedGroups.push(element.groupname); }); - this.bs.getResponses( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe( + this.bs.getResponses(selectedGroups).subscribe( (responseData: UnitResponse[]) => { if (responseData.length > 0) { const columnDelimiter = ';'; @@ -125,10 +118,7 @@ export class ResultsComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedGroups.push(element.groupname); }); - this.bs.getReviews( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe( + this.bs.getReviews(selectedGroups).subscribe( (responseData: ReviewData[]) => { if (responseData.length > 0) { // collect categories @@ -188,10 +178,7 @@ export class ResultsComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedGroups.push(element.groupname); }); - this.bs.getLogs( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe( + this.bs.getLogs(selectedGroups).subscribe( (responseData: LogData[]) => { if (responseData.length > 0) { const columnDelimiter = ';'; @@ -244,10 +231,7 @@ export class ResultsComponent implements OnInit { if (result !== false) { // ========================================================= this.dataLoading = true; - this.bs.deleteData( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedGroups).subscribe((deleteOk: boolean) => { + this.bs.deleteData(selectedGroups).subscribe((deleteOk: boolean) => { this.tableselectionCheckbox.clear(); this.dataLoading = false; }); diff --git a/src/app/admin/syscheck/syscheck.component.css b/src/app/workspace/syscheck/syscheck.component.css similarity index 100% rename from src/app/admin/syscheck/syscheck.component.css rename to src/app/workspace/syscheck/syscheck.component.css diff --git a/src/app/admin/syscheck/syscheck.component.html b/src/app/workspace/syscheck/syscheck.component.html similarity index 100% rename from src/app/admin/syscheck/syscheck.component.html rename to src/app/workspace/syscheck/syscheck.component.html diff --git a/src/app/admin/syscheck/syscheck.component.spec.ts b/src/app/workspace/syscheck/syscheck.component.spec.ts similarity index 100% rename from src/app/admin/syscheck/syscheck.component.spec.ts rename to src/app/workspace/syscheck/syscheck.component.spec.ts diff --git a/src/app/admin/syscheck/syscheck.component.ts b/src/app/workspace/syscheck/syscheck.component.ts similarity index 81% rename from src/app/admin/syscheck/syscheck.component.ts rename to src/app/workspace/syscheck/syscheck.component.ts index 03f06b82758f7299272eaff266e18844b386dbf0..d067ce41d6eeffcb4c675b7169e98cf1fc1729a4 100644 --- a/src/app/admin/syscheck/syscheck.component.ts +++ b/src/app/workspace/syscheck/syscheck.component.ts @@ -1,10 +1,10 @@ import { ConfirmDialogComponent, ConfirmDialogData } from './../../iqb-common/confirm-dialog/confirm-dialog.component'; import { Component, OnInit, ViewChild } from '@angular/core'; -import { BackendService, SysCheckStatistics } from './../backend.service'; -import { MainDatastoreService } from './../maindatastore.service'; +import { BackendService } from './../backend.service'; import { MatSnackBar, MatSort, MatTableDataSource, MatDialog } from '@angular/material'; import { SelectionModel } from '@angular/cdk/collections'; import { saveAs } from 'file-saver'; +import { SysCheckStatistics } from '../workspace.interfaces'; @Component({ @@ -23,24 +23,19 @@ export class SyscheckComponent implements OnInit { constructor( private bs: BackendService, - private mds: MainDatastoreService, private deleteConfirmDialog: MatDialog, public snackBar: MatSnackBar ) { - this.mds.isAdmin$.subscribe( - i => this.isAdmin = i); } ngOnInit() { - this.mds.adminToken$.subscribe(at => this.updateTable()); - this.mds.workspaceId$.subscribe(ws => this.updateTable()); - // console.log(saveAs); + this.updateTable(); } updateTable() { this.dataLoading = true; this.tableselectionCheckbox.clear(); - this.bs.getSysCheckReportList(this.mds.adminToken$.getValue(), this.mds.workspaceId$.getValue()).subscribe( + this.bs.getSysCheckReportList().subscribe( (resultData: SysCheckStatistics[]) => { this.dataLoading = false; this.resultDataSource = new MatTableDataSource<SysCheckStatistics>(resultData); @@ -69,10 +64,7 @@ export class SyscheckComponent implements OnInit { this.tableselectionCheckbox.selected.forEach(element => { selectedReports.push(element.id); }); - this.bs.getSysCheckReport( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedReports, ';', '"').subscribe( + this.bs.getSysCheckReport(selectedReports, ';', '"').subscribe( (reportData: string[]) => { if (reportData.length > 0) { const lineDelimiter = '\n'; @@ -118,10 +110,7 @@ export class SyscheckComponent implements OnInit { if (result !== false) { // ========================================================= this.dataLoading = true; - this.bs.deleteSysCheckReports( - this.mds.adminToken$.getValue(), - this.mds.workspaceId$.getValue(), - selectedReports).subscribe((deleteOk: boolean) => { + this.bs.deleteSysCheckReports(selectedReports).subscribe((deleteOk: boolean) => { this.tableselectionCheckbox.clear(); this.dataLoading = false; }); diff --git a/src/app/admin/admin-routing.module.ts b/src/app/workspace/workspace-routing.module.ts similarity index 62% rename from src/app/admin/admin-routing.module.ts rename to src/app/workspace/workspace-routing.module.ts index 91b25c935fba046c8f0f127242f1238635a02444..0761950b8766a9909714eabb091cdae0ff2f4e7d 100644 --- a/src/app/admin/admin-routing.module.ts +++ b/src/app/workspace/workspace-routing.module.ts @@ -3,20 +3,20 @@ import { MonitorComponent } from './monitor/monitor.component'; import { ResultsComponent } from './results/results.component'; import { NgModule, Component } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { MyfilesComponent } from './myfiles/myfiles.component'; -import { AdminComponent } from './admin.component'; +import { FilesComponent } from './files/files.component'; +import { WorkspaceComponent } from './workspace.component'; const routes: Routes = [ { - path: 'admin', - component: AdminComponent, + path: 'ws/:ws', + component: WorkspaceComponent, children: [ - {path: '', redirectTo: 'myfiles', pathMatch: 'full'}, - {path: 'myfiles', component: MyfilesComponent}, + {path: '', redirectTo: 'files', pathMatch: 'full'}, + {path: 'files', component: FilesComponent}, {path: 'syscheck', component: SyscheckComponent}, {path: 'monitor', component: MonitorComponent}, {path: 'results', component: ResultsComponent}, - {path: '**', component: MyfilesComponent} + {path: '**', component: FilesComponent} ] }]; @@ -25,4 +25,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) -export class AdminRoutingModule { } +export class WorkspaceRoutingModule { } diff --git a/src/app/workspace/workspace.component.css b/src/app/workspace/workspace.component.css new file mode 100644 index 0000000000000000000000000000000000000000..c695d220cec2d4010f3201155a1d90ffac8c917b --- /dev/null +++ b/src/app/workspace/workspace.component.css @@ -0,0 +1,40 @@ +/* --------------------------------------------- */ +#buttonsContainer { + color: white; + padding: 0 10px 0 0; +} +#buttonsContainer .material-icons { + font-size: 2.0rem; +} +#buttonsContainer img { + width: 100px; +} + +/* --------------------------------------------- */ +mat-toolbar { + position: fixed; + z-index: 100; + top: 4px; + right: 90px; +} + +#buttonsContainer .material-icons { + position: relative; + top: -8px; + font-size: 36px; + padding: 2px; +} + +.adminbackground { + flex: 10 0 900px; + box-shadow: 5px 10px 20px black; + background-color: white; + min-height: 85%; + margin: 15px; + padding: 25px; +} + +.communication-error-message { + color: red; + padding: 10px 50px; +} diff --git a/src/app/workspace/workspace.component.html b/src/app/workspace/workspace.component.html new file mode 100644 index 0000000000000000000000000000000000000000..45c74fcb4bf3330be84eef898096e8be44764f3d --- /dev/null +++ b/src/app/workspace/workspace.component.html @@ -0,0 +1,25 @@ +<div id="buttonsContainer" fxLayout="row" fxLayoutAlign="space-between center"> + <a [routerLink]="['/']"> + <img src="assets/IQB-LogoA.png" matTooltip="Startseite"/> + </a> + <div class="error-msg">{{ (mds.globalErrorMsg$ | async)?.labelNice }}</div> + <div>IQB-Testcenter Verwaltung</div> + <div>{{ pageTitle }}</div> +</div> +<div class="page-body"> + <div class="adminbackground"> + + + <nav mat-tab-nav-bar> + <a mat-tab-link + *ngFor="let link of navLinks" + [routerLink]="link.path" + routerLinkActive #rla="routerLinkActive" + [active]="rla.isActive"> + {{link.label}} + </a> + </nav> + + <router-outlet></router-outlet> + </div> +</div> diff --git a/src/app/admin/myfiles/myfiles.component.spec.ts b/src/app/workspace/workspace.component.spec.ts similarity index 55% rename from src/app/admin/myfiles/myfiles.component.spec.ts rename to src/app/workspace/workspace.component.spec.ts index 86fce73a6b54c0ca626fc85b87df4306d1ebe284..4ea31a8f29e3256b71a7d1772483fae203751971 100644 --- a/src/app/admin/myfiles/myfiles.component.spec.ts +++ b/src/app/workspace/workspace.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { MyfilesComponent } from './myfiles.component'; +import { WorkspaceComponent } from './workspace.component'; -describe('MyfilesComponent', () => { - let component: MyfilesComponent; - let fixture: ComponentFixture<MyfilesComponent>; +describe('WorkspaceComponent', () => { + let component: WorkspaceComponent; + let fixture: ComponentFixture<WorkspaceComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MyfilesComponent ] + declarations: [ WorkspaceComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(MyfilesComponent); + fixture = TestBed.createComponent(WorkspaceComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/workspace/workspace.component.ts b/src/app/workspace/workspace.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..2700ee2573e5486fb959e9e7688733d5ebea07be --- /dev/null +++ b/src/app/workspace/workspace.component.ts @@ -0,0 +1,63 @@ +import { WorkspaceDataService } from './workspacedata.service'; +import { MainDataService } from './../maindata.service'; +import { ActivatedRoute } from '@angular/router'; +import { Observable, Subscription } from 'rxjs'; +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { MatTabsModule, MatSelectModule, MatFormFieldModule } from '@angular/material'; + + +@Component({ + templateUrl: './workspace.component.html', + styleUrls: ['./workspace.component.css'] +}) +export class WorkspaceComponent implements OnInit, OnDestroy { + public navLinks = [ + {path: 'files', label: 'Dateien'}, + {path: 'syscheck', label: 'System-Check Berichte'}, + {path: 'monitor', label: 'Monitor'}, + {path: 'results', label: 'Ergebnisse'} + ]; + + public pageTitle = ''; + private routingSubscription: Subscription = null; + private logindataSubscription: Subscription = null; + + // CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC + constructor( + private route: ActivatedRoute, + private mds: MainDataService, + private wds: WorkspaceDataService + ) { } + + // CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC + ngOnInit() { + this.routingSubscription = this.route.params.subscribe(params => { + const ws = Number(params['ws']); + this.wds.setWorkspaceId(ws); + if ((this.mds.adminToken.length > 0) && (ws > 0)) { + this.pageTitle = this.mds.getWorkspaceName(ws) + ' (' + this.mds.getWorkspaceRole(ws) + ')'; + } else { + this.pageTitle = ''; + } + }); + + this.logindataSubscription = this.mds.loginData$.subscribe(ld => { + const ws = this.wds.ws; + if (ws > 0) { + this.pageTitle = this.mds.getWorkspaceName(ws) + ' (' + this.mds.getWorkspaceRole(ws) + ')'; + } else { + this.pageTitle = ''; + } + }); + } + + // % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + ngOnDestroy() { + if (this.routingSubscription !== null) { + this.routingSubscription.unsubscribe(); + } + if (this.logindataSubscription !== null) { + this.logindataSubscription.unsubscribe(); + } + } +} diff --git a/src/app/workspace/workspace.interceptor.ts b/src/app/workspace/workspace.interceptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..01876554a3aaaf629d912760e06601bd1a868171 --- /dev/null +++ b/src/app/workspace/workspace.interceptor.ts @@ -0,0 +1,34 @@ +import { WorkspaceDataService } from './workspacedata.service'; +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, + HttpHandler, HttpEvent, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable() +export class WorkspaceInterceptor implements HttpInterceptor { + constructor(public wds: WorkspaceDataService) {} + + intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { + let authDataStr = request.headers.get('AuthToken'); + let authData = {}; + if (authDataStr) { + authData = JSON.parse(authDataStr); + } + + const ws = this.wds.workspaceId$.getValue(); + if (ws >= 0) { + authData['ws'] = ws; + } + const requestA = request.clone({ + setHeaders: { + AuthToken: JSON.stringify(authData) + } + }); + + return next.handle(requestA); + } +} + +export const httpInterceptorProviders = [ + { provide: HTTP_INTERCEPTORS, useClass: WorkspaceInterceptor, multi: true }, +]; diff --git a/src/app/workspace/workspace.interfaces.ts b/src/app/workspace/workspace.interfaces.ts new file mode 100644 index 0000000000000000000000000000000000000000..2053eb378e34b29422e32db3f7f024d848f9d045 --- /dev/null +++ b/src/app/workspace/workspace.interfaces.ts @@ -0,0 +1,92 @@ +export interface GetFileResponseData { + filename: string; + filesize: number; + filesizestr: string; + filedatetime: string; + filedatetimestr: string; + type: string; + typelabel: string; + isChecked: boolean; +} + +export interface CheckWorkspaceResponseData { + errors: string[]; + infos: string[]; + warnings: string[]; +} + + +export interface GroupResponse { + name: string; + testsTotal: number; + testsStarted: number; + responsesGiven: number; +} + +export interface BookletsStarted { + groupname: string; + loginname: string; + code: string; + bookletname: string; + locked: boolean; +} + +export interface UnitResponse { + groupname: string; + loginname: string; + code: string; + bookletname: string; + unitname: string; + responses: string; + restorepoint: string; + responsetype: string; + responses_ts: number; + restorepoint_ts: number; + laststate: string; +} + +export interface MonitorData { + groupname: string; + loginsPrepared: number; + personsPrepared: number; + bookletsPrepared: number; + bookletsStarted: number; + bookletsLocked: number; +} + +export interface ResultData { + groupname: string; + bookletsStarted: number; + num_units_min: number; + num_units_max: number; + num_units_mean: number; +} + +export interface LogData { + groupname: string; + loginname: string; + code: string; + bookletname: string; + unitname: string; + timestamp: number; + logentry: string; +} + +export interface ReviewData { + groupname: string; + loginname: string; + code: string; + bookletname: string; + unitname: string; + priority: number; + categories: string; + reviewtime: Date; + entry: string; +} + +export interface SysCheckStatistics { + id: string; + label: string; + count: number; + details: string[]; +} diff --git a/src/app/admin/admin.module.ts b/src/app/workspace/workspace.module.ts similarity index 78% rename from src/app/admin/admin.module.ts rename to src/app/workspace/workspace.module.ts index d349c5e875546ca379949a6f18f1887e8be3b243..01832629f0954ce34ef9e70c1a1b54bd1516fb57 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/workspace/workspace.module.ts @@ -5,14 +5,14 @@ import { IqbCommonModule } from '../iqb-common'; import { ReactiveFormsModule } from '@angular/forms'; import { NgModule, Component } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MainDatastoreService } from './maindatastore.service'; +import { WorkspaceDataService } from './workspacedata.service'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClientModule } from '@angular/common/http'; -import { AdminRoutingModule } from './admin-routing.module'; -import { AdminComponent } from './admin.component'; -import { MyfilesComponent } from './myfiles/myfiles.component'; +import { WorkspaceRoutingModule } from './workspace-routing.module'; +import { WorkspaceComponent } from './workspace.component'; +import { FilesComponent } from './files/files.component'; import { ResultsComponent } from './results/results.component'; @@ -23,12 +23,13 @@ import { MonitorComponent } from './monitor/monitor.component'; import { MatExpansionModule } from '@angular/material/expansion'; import {MatGridListModule} from '@angular/material/grid-list'; import { SyscheckComponent } from './syscheck/syscheck.component'; +import { httpInterceptorProviders } from './workspace.interceptor'; @NgModule({ imports: [ IqbFilesModule, CommonModule, - AdminRoutingModule, + WorkspaceRoutingModule, MatTableModule, MatTabsModule, MatIconModule, @@ -55,19 +56,20 @@ import { SyscheckComponent } from './syscheck/syscheck.component'; FlexLayoutModule ], exports: [ - AdminComponent + WorkspaceComponent ], declarations: [ - AdminComponent, - MyfilesComponent, + WorkspaceComponent, + FilesComponent, ResultsComponent, MonitorComponent, SyscheckComponent ], providers: [ BackendService, - MainDatastoreService + httpInterceptorProviders, + WorkspaceDataService ], }) -export class AdminModule { } +export class WorkspaceModule { } diff --git a/src/app/workspace/workspacedata.service.spec.ts b/src/app/workspace/workspacedata.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4efb2cb6ef585b014ba53d398f1defc114ef4ac8 --- /dev/null +++ b/src/app/workspace/workspacedata.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { WorkspaceDataService } from './workspacedata.service'; + +describe('WorkspaceDataService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [WorkspaceDataService] + }); + }); + + it('should be created', inject([WorkspaceDataService], (service: WorkspaceDataService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/workspace/workspacedata.service.ts b/src/app/workspace/workspacedata.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..3aee6f832ef889dab29245eb64fbe3f476960d9d --- /dev/null +++ b/src/app/workspace/workspacedata.service.ts @@ -0,0 +1,72 @@ +import { MainDataService } from './../maindata.service'; +// import { Observable } from 'rxjs/Observable'; +// import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { BehaviorSubject } from 'rxjs'; +import { FormGroup } from '@angular/forms'; +import { Injectable, Component, Input, Output, EventEmitter } from '@angular/core'; +import { MatDialog, MatDialogRef } from '@angular/material'; +import { Router, ActivatedRoute } from '@angular/router'; + +import { IqbCommonModule, ConfirmDialogComponent, ConfirmDialogData } from '../iqb-common'; +import { BackendService } from './backend.service'; +import { WorkspaceData } from '../app.interfaces'; +import { ServerError } from '../backend.service'; + +@Injectable({ + providedIn: 'root' +}) + +export class WorkspaceDataService { + public workspaceId$ = new BehaviorSubject<number>(-1); + public globalErrorMsg$ = new BehaviorSubject<ServerError>(null); + + public get ws() : number { + return this.workspaceId$.getValue(); + } + + + // ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc + constructor ( + public confirmDialog: MatDialog, + private bs: BackendService, + private mds: MainDataService, + private route: ActivatedRoute, + private router: Router + ) { } + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + setNewErrorMsg(err: ServerError = null) { + this.globalErrorMsg$.next(err); + } + + // ******************************************************************************************************* + logout() { + // const dialogRef = this.confirmDialog.open(ConfirmDialogComponent, { + // width: '400px', + // height: '300px', + // data: <ConfirmDialogData>{ + // title: 'Abmelden', + // content: 'Möchten Sie sich abmelden?', + // confirmbuttonlabel: 'Abmelden' + // } + // }); + // dialogRef.afterClosed().subscribe(result => { + // if (result !== false) { + // this.bs.logout(this.adminToken$.getValue()).subscribe( + // logoutresponse => { + // this.updateAdminStatus('', '', [], false, ''); + // this.router.navigateByUrl('/'); + // }, (err: ServerError) => { + // this.updateAdminStatus('', '', [], false, err.label); + // this.router.navigateByUrl('/'); + // } + // ); + // } + // }); + } + + // ******************************************************************************************************* + setWorkspaceId(newId: number) { + this.workspaceId$.next(newId); + } +} diff --git a/src/environments/environment.build.ts b/src/environments/environment.build.ts index 76362cf3f1d5b4e7ff0a187ab2ca94479e32803a..82d651cf790b6dc453f468cd4e9b2b6560252d1c 100644 --- a/src/environments/environment.build.ts +++ b/src/environments/environment.build.ts @@ -2,7 +2,7 @@ export const environment = { production: false, - testcenterUrl: '/', + testcenterUrl: '/admin/', appName: 'IQB-Testcenter Verwaltung', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', appVersion: '0.10 - 15.3.2019' diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 35580725320bf64889fa23b2add8b3e91c234cfd..2029f1e45a93749c365860a262868c59dd79ff8a 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,6 +1,6 @@ export const environment = { production: true, - testcenterUrl: '/', + testcenterUrl: '/admin/', appName: 'IQB-Testcenter-Verwaltung', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', appVersion: '0 (prod)' diff --git a/src/environments/environment.ts b/src/environments/environment.ts index d2f188a0d2a42ffaecaae1e0486f2e402d914d49..82b5a8a4500c447161229a4b94220e3fd9ca0cf2 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,7 +5,7 @@ export const environment = { production: false, - testcenterUrl: 'https://itemdb2.iqb.hu-berlin.de/', + testcenterUrl: 'https://itemdb2.iqb.hu-berlin.de/admin/', appName: 'IQB-Testcenter Verwaltung', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', appVersion: '0.5 (dev)' diff --git a/src/iqb-theme2.scss b/src/iqb-theme2.scss index c27f19130cab1d2674b37bfa0c4576d79b1b88c9..215f8b27c77f1f2d867bd7be5cd7a5ddf44db16b 100644 --- a/src/iqb-theme2.scss +++ b/src/iqb-theme2.scss @@ -12,5 +12,5 @@ body { height: 100%; margin: 0; font-family: "Orienta"; - background: #003333; + background: linear-gradient(to left, #003333, #045659, #0d7b84, #1aa2b2, #2acae5) } diff --git a/src/styles.css b/src/styles.css index 94b3577a53cb3b234c91da7fbfb872d68a6b5aee..a7578e00f336c912d31f0ff12604b768e84648b7 100644 --- a/src/styles.css +++ b/src/styles.css @@ -12,15 +12,8 @@ overflow-x: auto; position: absolute; width: 100%; - top: 70px; + top: 60px; bottom: 0; - background: linear-gradient(to left, #003333, #045659, #0d7b84, #1aa2b2, #2acae5) - /* background-image: linear-gradient(to bottom, #003333, #00465d, #005791, #005ebe, #8854d0); */ - /* display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: flex-start; - justify-content: left; */ } @@ -37,3 +30,10 @@ right: 0; } +.logo img { + width: 100px; +} + +.error-msg { + color: brown; +}