From b5b3e60e81002f889a1124a0f01289c3bcacce9a Mon Sep 17 00:00:00 2001 From: GabrielMeyer Date: Fri, 26 Jul 2019 11:47:04 +0200 Subject: [PATCH] Fixes some UI issues corresponding to the theme - Fixes white page at 'legalnotice' and 'privacypolicy' - Fixes the button to collapse the sidenav - Fixes a too long text in the headbar - Reworked the login data service: * make order of operations clear * prevent setting invalid data into the storage --- .../core/core-services/pwa.service.spec.ts | 10 +- .../core-services/storage.service.spec.ts | 2 + .../core/ui-services/duration.service.spec.ts | 10 +- .../ui-services/html-to-pdf.service.spec.ts | 17 +- .../ui-services/load-font.service.spec.ts | 17 +- .../ui-services/login-data.service.spec.ts | 2 + .../core/ui-services/login-data.service.ts | 181 +++++++++++++----- .../ui-services/media-manage.service.spec.ts | 16 +- .../ui-services/pdf-document.service.spec.ts | 18 +- .../app/core/ui-services/poll.service.spec.ts | 17 +- .../ui-services/routing-state.service.spec.ts | 18 +- .../core/ui-services/spinner.service.spec.ts | 17 +- .../core/ui-services/theme.service.spec.ts | 17 +- .../src/app/core/ui-services/theme.service.ts | 13 -- .../core/ui-services/update.service.spec.ts | 7 +- .../core/ui-services/viewport.service.spec.ts | 2 + .../xlsx-export-service.service.spec.ts | 17 +- .../head-bar/head-bar.component.scss | 1 + .../legal-notice-content.component.html | 5 +- .../legal-notice-content.component.ts | 14 +- .../shared/components/logo/logo.component.ts | 2 +- .../privacy-policy-content.component.ts | 10 +- .../login-mask/login-mask.component.ts | 30 +-- .../login-wrapper/login-wrapper.component.ts | 8 +- .../reset-password-confirm.component.html | 8 +- .../reset-password.component.html | 9 +- .../reset-password.component.ts | 5 +- client/src/app/site/site.component.scss | 1 + openslides/users/views.py | 11 +- tests/integration/users/test_views.py | 2 +- 30 files changed, 294 insertions(+), 193 deletions(-) diff --git a/client/src/app/core/core-services/pwa.service.spec.ts b/client/src/app/core/core-services/pwa.service.spec.ts index 6faa381f6..de53e7233 100644 --- a/client/src/app/core/core-services/pwa.service.spec.ts +++ b/client/src/app/core/core-services/pwa.service.spec.ts @@ -1,4 +1,4 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { PwaService } from './pwa.service'; import { E2EImportsModule } from 'e2e-imports.module'; @@ -6,12 +6,12 @@ import { E2EImportsModule } from 'e2e-imports.module'; describe('PwaService', () => { beforeEach(() => TestBed.configureTestingModule({ - imports: [E2EImportsModule] + imports: [E2EImportsModule], + providers: [PwaService] }) ); - it('should be created', () => { - const service: PwaService = TestBed.get(PwaService); + it('should be created', inject([PwaService], (service: PwaService) => { expect(service).toBeTruthy(); - }); + })); }); diff --git a/client/src/app/core/core-services/storage.service.spec.ts b/client/src/app/core/core-services/storage.service.spec.ts index 5f68fdff2..6181f407c 100644 --- a/client/src/app/core/core-services/storage.service.spec.ts +++ b/client/src/app/core/core-services/storage.service.spec.ts @@ -1,10 +1,12 @@ import { TestBed, inject } from '@angular/core/testing'; import { StorageService } from './storage.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('StorageService', () => { beforeEach(() => { TestBed.configureTestingModule({ + imports: [E2EImportsModule], providers: [StorageService] }); }); diff --git a/client/src/app/core/ui-services/duration.service.spec.ts b/client/src/app/core/ui-services/duration.service.spec.ts index 11a4c8418..de2a5ac43 100644 --- a/client/src/app/core/ui-services/duration.service.spec.ts +++ b/client/src/app/core/ui-services/duration.service.spec.ts @@ -1,4 +1,4 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { DurationService } from './duration.service'; import { E2EImportsModule } from 'e2e-imports.module'; @@ -6,12 +6,12 @@ import { E2EImportsModule } from 'e2e-imports.module'; describe('DurationService', () => { beforeEach(() => TestBed.configureTestingModule({ - imports: [E2EImportsModule] + imports: [E2EImportsModule], + providers: [DurationService] }) ); - it('should be created', () => { - const service: DurationService = TestBed.get(DurationService); + it('should be created', inject([DurationService], (service: DurationService) => { expect(service).toBeTruthy(); - }); + })); }); diff --git a/client/src/app/core/ui-services/html-to-pdf.service.spec.ts b/client/src/app/core/ui-services/html-to-pdf.service.spec.ts index 72411a9fb..353d076d0 100644 --- a/client/src/app/core/ui-services/html-to-pdf.service.spec.ts +++ b/client/src/app/core/ui-services/html-to-pdf.service.spec.ts @@ -1,12 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { HtmlToPdfService } from './html-to-pdf.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('HtmlToPdfService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: HtmlToPdfService = TestBed.get(HtmlToPdfService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [HtmlToPdfService] + }); }); + + it('should be created', inject([HtmlToPdfService], (service: HtmlToPdfService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/load-font.service.spec.ts b/client/src/app/core/ui-services/load-font.service.spec.ts index 114713f42..063962f60 100644 --- a/client/src/app/core/ui-services/load-font.service.spec.ts +++ b/client/src/app/core/ui-services/load-font.service.spec.ts @@ -1,12 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { LoadFontService } from './load-font.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('LoadFontService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: LoadFontService = TestBed.get(LoadFontService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [LoadFontService] + }); }); + + it('should be created', inject([LoadFontService], (service: LoadFontService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/login-data.service.spec.ts b/client/src/app/core/ui-services/login-data.service.spec.ts index 7dcf55ab6..29b48fa69 100644 --- a/client/src/app/core/ui-services/login-data.service.spec.ts +++ b/client/src/app/core/ui-services/login-data.service.spec.ts @@ -1,10 +1,12 @@ import { TestBed, inject } from '@angular/core/testing'; import { LoginDataService } from './login-data.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('LoginDataService', () => { beforeEach(() => { TestBed.configureTestingModule({ + imports: [E2EImportsModule], providers: [LoginDataService] }); }); diff --git a/client/src/app/core/ui-services/login-data.service.ts b/client/src/app/core/ui-services/login-data.service.ts index 2d2403a25..909c1a283 100644 --- a/client/src/app/core/ui-services/login-data.service.ts +++ b/client/src/app/core/ui-services/login-data.service.ts @@ -1,22 +1,35 @@ -import { Injectable } from '@angular/core'; +import { Injectable, EventEmitter } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { ConfigService } from './config.service'; import { StorageService } from '../core-services/storage.service'; import { OpenSlidesStatusService } from '../core-services/openslides-status.service'; +import { HttpService } from '../core-services/http.service'; +import { environment } from 'environments/environment.prod'; +import { auditTime } from 'rxjs/operators'; /** * The login data send by the server. */ export interface LoginData { - privacy_policy: string; - legal_notice: string; + privacy_policy?: string; + legal_notice?: string; theme: string; logo_web_header: { path: string; display_name: string; }; + login_info_text?: string; +} + +/** + * Checks, if the given object holds valid LoginData. + * + * @param obj The object to check + */ +function isLoginData(obj: any): obj is LoginData { + return !!obj && obj.theme && obj.logo_web_header; } const LOGIN_DATA_STORAGE_KEY = 'LoginData'; @@ -29,28 +42,40 @@ const LOGIN_DATA_STORAGE_KEY = 'LoginData'; providedIn: 'root' }) export class LoginDataService { + /** + * Holds the installation notice. + */ + private readonly _loginInfoText = new BehaviorSubject(''); + + /** + * Returns the installation notice as observable. + */ + public get loginInfoText(): Observable { + return this._loginInfoText.asObservable(); + } + /** * Holds the privacy policy */ - private readonly _privacy_policy = new BehaviorSubject(''); + private readonly _privacyPolicy = new BehaviorSubject(''); /** * Returns an observable for the privacy policy */ - public get privacy_policy(): Observable { - return this._privacy_policy.asObservable(); + public get privacyPolicy(): Observable { + return this._privacyPolicy.asObservable(); } /** * Holds the legal notice */ - private readonly _legal_notice = new BehaviorSubject(''); + private readonly _legalNotice = new BehaviorSubject(''); /** * Returns an observable for the legal notice */ - public get legal_notice(): Observable { - return this._legal_notice.asObservable(); + public get legalNotice(): Observable { + return this._legalNotice.asObservable(); } /** @@ -68,7 +93,7 @@ export class LoginDataService { /** * Holds the custom web header */ - private readonly _logo_web_header = new BehaviorSubject<{ path: string; display_name: string }>({ + private readonly _logoWebHeader = new BehaviorSubject<{ path: string; display_name: string }>({ path: '', display_name: '' }); @@ -76,10 +101,27 @@ export class LoginDataService { /** * Returns an observable for the web header */ - public get logo_web_header(): Observable<{ path: string; display_name: string }> { - return this._logo_web_header.asObservable(); + public get logoWebHeader(): Observable<{ path: string; display_name: string }> { + return this._logoWebHeader.asObservable(); } + /** + * Emit this event, if the current login data should be stored. This + * is debounced to minimize requests to the storage service. + */ + private storeLoginDataRequests = new EventEmitter(); + + /** + * Holds, if `_refresh` can be called. This will be true fter the setup. + */ + private canRefresh = false; + + /** + * Marks, if during the etup (with `canRefresh=false`) a refresh was requested. + * After the setup, this variabel will be checked and a refresh triggered, if it is true. + */ + private markRefresh = false; + /** * Constructs this service. The config service is needed to update the privacy * policy and legal notice, when their config values change. @@ -88,67 +130,112 @@ export class LoginDataService { public constructor( private configService: ConfigService, private storageService: StorageService, - private OSStatus: OpenSlidesStatusService + private OSStatus: OpenSlidesStatusService, + private httpService: HttpService ) { + this.storeLoginDataRequests.pipe(auditTime(100)).subscribe(() => this.storeLoginData()); + this.setup(); + } + + /** + * Loads the login data and *after* that the configs are subscribed. If a request for a refresh + * was issued while the setup, the refresh will be executed afterwards. + */ + private async setup(): Promise { + await this.loadLoginData(); this.configService.get('general_event_privacy_policy').subscribe(value => { - this._privacy_policy.next(value); - this.storeLoginData(); + if (value !== undefined) { + this._privacyPolicy.next(value); + this.storeLoginDataRequests.next(); + } }); this.configService.get('general_event_legal_notice').subscribe(value => { - this._legal_notice.next(value); - this.storeLoginData(); + if (value !== undefined) { + this._legalNotice.next(value); + this.storeLoginDataRequests.next(); + } }); - configService.get('openslides_theme').subscribe(value => { - this._theme.next(value); - this.storeLoginData(); + this.configService.get('openslides_theme').subscribe(value => { + if (value) { + this._theme.next(value); + this.storeLoginDataRequests.next(); + } }); - configService.get<{ path: string; display_name: string }>('logo_web_header').subscribe(value => { - this._logo_web_header.next(value); - this.storeLoginData(); + this.configService.get<{ path: string; display_name: string }>('logo_web_header').subscribe(value => { + if (value) { + this._logoWebHeader.next(value); + this.storeLoginDataRequests.next(); + } }); + this.canRefresh = true; + if (this.markRefresh) { + this._refresh(); + } + } - this.loadLoginData(); + /** + * Explicit refresh the ata from the server. + */ + public refresh(): void { + if (this.canRefresh && !this.markRefresh) { + this._refresh(); + } else if (!this.canRefresh) { + this.markRefresh = true; + } + } + + /** + * The actual refresh implementation. + */ + private async _refresh(): Promise { + try { + const loginData = await this.httpService.get(environment.urlPrefix + '/users/login/'); + this.setLoginData(loginData); + this.storeLoginDataRequests.next(); + } catch (e) { + console.log('Could not refresh login data', e); + } + this.markRefresh = false; } /** * Load the login data from the storage. If it there, set it. */ private async loadLoginData(): Promise { - const loginData = await this.storageService.get(LOGIN_DATA_STORAGE_KEY); - if (loginData) { + const loginData = await this.storageService.get(LOGIN_DATA_STORAGE_KEY); + if (isLoginData(loginData)) { this.setLoginData(loginData); } } /** - * Setter for the login data + * Triggers all subjects with the given data. * - * @param loginData the login data + * @param loginData The data */ - public setLoginData(loginData: LoginData): void { - this._privacy_policy.next(loginData.privacy_policy); - this._legal_notice.next(loginData.legal_notice); + private setLoginData(loginData: LoginData): void { + this._privacyPolicy.next(loginData.privacy_policy); + this._legalNotice.next(loginData.legal_notice); this._theme.next(loginData.theme); - this.storeLoginData(loginData); + this._logoWebHeader.next(loginData.logo_web_header); + this._loginInfoText.next(loginData.login_info_text); } /** - * Saves the login data in the storage. - * - * @param loginData If given, this data is used. If it's null, the current values - * from the behaviour subject are taken. + * Saves the login data to the storeage. Do not call this method and + * use `storeLoginDataRequests` instead. The data to store will be + * taken form all subjects. */ - private storeLoginData(loginData?: LoginData): void { - if (!loginData) { - loginData = { - privacy_policy: this._privacy_policy.getValue(), - legal_notice: this._legal_notice.getValue(), - theme: this._theme.getValue(), - logo_web_header: this._logo_web_header.getValue() - }; - } - if (!this.OSStatus.isInHistoryMode) { - this.storageService.set(LOGIN_DATA_STORAGE_KEY, loginData); + private storeLoginData(): void { + if (this.OSStatus.isInHistoryMode) { + return; } + const loginData = { + privacy_policy: this._privacyPolicy.getValue(), + legal_notice: this._legalNotice.getValue(), + theme: this._theme.getValue(), + logo_web_header: this._logoWebHeader.getValue() + }; + this.storageService.set(LOGIN_DATA_STORAGE_KEY, loginData); } } diff --git a/client/src/app/core/ui-services/media-manage.service.spec.ts b/client/src/app/core/ui-services/media-manage.service.spec.ts index 6b867cfe3..dbf68f3bf 100644 --- a/client/src/app/core/ui-services/media-manage.service.spec.ts +++ b/client/src/app/core/ui-services/media-manage.service.spec.ts @@ -1,13 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { MediaManageService } from './media-manage.service'; import { E2EImportsModule } from 'e2e-imports.module'; describe('MediaManageService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: MediaManageService = TestBed.get(MediaManageService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [MediaManageService] + }); }); + + it('should be created', inject([MediaManageService], (service: MediaManageService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/pdf-document.service.spec.ts b/client/src/app/core/ui-services/pdf-document.service.spec.ts index d420e6449..90cfb86a7 100644 --- a/client/src/app/core/ui-services/pdf-document.service.spec.ts +++ b/client/src/app/core/ui-services/pdf-document.service.spec.ts @@ -1,17 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { PdfDocumentService } from '../ui-services/pdf-document.service'; import { E2EImportsModule } from 'e2e-imports.module'; describe('PdfDocumentService', () => { - beforeEach(() => + beforeEach(() => { TestBed.configureTestingModule({ - imports: [E2EImportsModule] - }) - ); - - it('should be created', () => { - const service: PdfDocumentService = TestBed.get(PdfDocumentService); - expect(service).toBeTruthy(); + imports: [E2EImportsModule], + providers: [PdfDocumentService] + }); }); + + it('should be created', inject([PdfDocumentService], (service: PdfDocumentService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/poll.service.spec.ts b/client/src/app/core/ui-services/poll.service.spec.ts index 35d93f8ad..1f92b8650 100644 --- a/client/src/app/core/ui-services/poll.service.spec.ts +++ b/client/src/app/core/ui-services/poll.service.spec.ts @@ -1,12 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { PollService } from './poll.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('PollService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: PollService = TestBed.get(PollService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [PollService] + }); }); + + it('should be created', inject([PollService], (service: PollService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/routing-state.service.spec.ts b/client/src/app/core/ui-services/routing-state.service.spec.ts index c7fd3c949..54efeadf7 100644 --- a/client/src/app/core/ui-services/routing-state.service.spec.ts +++ b/client/src/app/core/ui-services/routing-state.service.spec.ts @@ -1,17 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { RoutingStateService } from './routing-state.service'; import { E2EImportsModule } from 'e2e-imports.module'; describe('RoutingStateService', () => { - beforeEach(() => + beforeEach(() => { TestBed.configureTestingModule({ - imports: [E2EImportsModule] - }) - ); - - it('should be created', () => { - const service: RoutingStateService = TestBed.get(RoutingStateService); - expect(service).toBeTruthy(); + imports: [E2EImportsModule], + providers: [RoutingStateService] + }); }); + + it('should be created', inject([RoutingStateService], (service: RoutingStateService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/spinner.service.spec.ts b/client/src/app/core/ui-services/spinner.service.spec.ts index ca42d5aef..c75923652 100644 --- a/client/src/app/core/ui-services/spinner.service.spec.ts +++ b/client/src/app/core/ui-services/spinner.service.spec.ts @@ -1,12 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { SpinnerService } from './spinner.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('SpinnerService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: SpinnerService = TestBed.get(SpinnerService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [SpinnerService] + }); }); + + it('should be created', inject([SpinnerService], (service: SpinnerService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/theme.service.spec.ts b/client/src/app/core/ui-services/theme.service.spec.ts index 51fe21708..a3cbebe95 100644 --- a/client/src/app/core/ui-services/theme.service.spec.ts +++ b/client/src/app/core/ui-services/theme.service.spec.ts @@ -1,12 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { ThemeService } from './theme.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('ThemeService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: ThemeService = TestBed.get(ThemeService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [ThemeService] + }); }); + + it('should be created', inject([ThemeService], (service: ThemeService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/core/ui-services/theme.service.ts b/client/src/app/core/ui-services/theme.service.ts index a92de283e..fabf3b125 100644 --- a/client/src/app/core/ui-services/theme.service.ts +++ b/client/src/app/core/ui-services/theme.service.ts @@ -77,17 +77,4 @@ export class ThemeService { return null; } } - - /** - * Function to ensure, that there is at least one theme set to define - * the colors of the components. - * - * If a theme is already set, nothing happens, otherwise the - * `DEFAULT_THEME` will be set. - */ - public checkTheme(): void { - if (!this.currentTheme || this.currentTheme === '') { - this.changeTheme(ThemeService.DEFAULT_THEME); - } - } } diff --git a/client/src/app/core/ui-services/update.service.spec.ts b/client/src/app/core/ui-services/update.service.spec.ts index 556f4ff6e..4355533c5 100644 --- a/client/src/app/core/ui-services/update.service.spec.ts +++ b/client/src/app/core/ui-services/update.service.spec.ts @@ -1,4 +1,4 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { UpdateService } from './update.service'; import { E2EImportsModule } from 'e2e-imports.module'; @@ -11,8 +11,7 @@ describe('UpdateService', () => { }) ); - it('should be created', () => { - const service: UpdateService = TestBed.get(UpdateService); + it('should be created', inject([UpdateService], (service: UpdateService) => { expect(service).toBeTruthy(); - }); + })); }); diff --git a/client/src/app/core/ui-services/viewport.service.spec.ts b/client/src/app/core/ui-services/viewport.service.spec.ts index 9bf530451..c58b2937f 100644 --- a/client/src/app/core/ui-services/viewport.service.spec.ts +++ b/client/src/app/core/ui-services/viewport.service.spec.ts @@ -1,10 +1,12 @@ import { TestBed, inject } from '@angular/core/testing'; import { ViewportService } from './viewport.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('ViewportService', () => { beforeEach(() => { TestBed.configureTestingModule({ + imports: [E2EImportsModule], providers: [ViewportService] }); }); diff --git a/client/src/app/core/ui-services/xlsx-export-service.service.spec.ts b/client/src/app/core/ui-services/xlsx-export-service.service.spec.ts index efa280dfb..4e7bfeefd 100644 --- a/client/src/app/core/ui-services/xlsx-export-service.service.spec.ts +++ b/client/src/app/core/ui-services/xlsx-export-service.service.spec.ts @@ -1,12 +1,17 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { XlsxExportServiceService } from './xlsx-export-service.service'; +import { E2EImportsModule } from 'e2e-imports.module'; describe('XlsxExportServiceService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: XlsxExportServiceService = TestBed.get(XlsxExportServiceService); - expect(service).toBeTruthy(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [XlsxExportServiceService] + }); }); + + it('should be created', inject([XlsxExportServiceService], (service: XlsxExportServiceService) => { + expect(service).toBeTruthy(); + })); }); diff --git a/client/src/app/shared/components/head-bar/head-bar.component.scss b/client/src/app/shared/components/head-bar/head-bar.component.scss index 79a35285f..f6292214d 100644 --- a/client/src/app/shared/components/head-bar/head-bar.component.scss +++ b/client/src/app/shared/components/head-bar/head-bar.component.scss @@ -13,6 +13,7 @@ .toolbar-left { display: flex; + max-width: calc(100% - 100px); button { margin: 12px 0; } diff --git a/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.html b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.html index 1d97f3d0a..1820b667b 100644 --- a/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.html +++ b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.html @@ -1,6 +1,9 @@
- + +
+ The event manager hasn't set up a legal notice yet. +
diff --git a/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.ts b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.ts index 0cb2d5340..7d47ad4e8 100644 --- a/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.ts +++ b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.ts @@ -1,7 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; - import { LoginDataService } from 'app/core/ui-services/login-data.service'; import { environment } from 'environments/environment'; import { HttpService } from 'app/core/core-services/http.service'; @@ -82,20 +80,14 @@ export class LegalNoticeContentComponent implements OnInit { * @param translate * @param http */ - public constructor( - private loginDataService: LoginDataService, - private translate: TranslateService, - private http: HttpService - ) {} + public constructor(private loginDataService: LoginDataService, private http: HttpService) {} /** * Subscribes for the legal notice text. */ public ngOnInit(): void { - this.loginDataService.legal_notice.subscribe(legalNotice => { - if (legalNotice) { - this.legalNotice = this.translate.instant(legalNotice); - } + this.loginDataService.legalNotice.subscribe(legalNotice => { + this.legalNotice = legalNotice; }); // Query the version info. diff --git a/client/src/app/shared/components/logo/logo.component.ts b/client/src/app/shared/components/logo/logo.component.ts index 3d6c21832..b986bc639 100644 --- a/client/src/app/shared/components/logo/logo.component.ts +++ b/client/src/app/shared/components/logo/logo.component.ts @@ -42,7 +42,7 @@ export class LogoComponent implements OnInit, OnDestroy { * On init method */ public ngOnInit(): void { - this.logoSubscription = this.loginDataService.logo_web_header.subscribe(nextLogo => { + this.logoSubscription = this.loginDataService.logoWebHeader.subscribe(nextLogo => { if (nextLogo) { this.logoPath = nextLogo.path; } diff --git a/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.ts b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.ts index 456935e44..c52c10263 100644 --- a/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.ts +++ b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.ts @@ -1,7 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; - import { LoginDataService } from 'app/core/ui-services/login-data.service'; /** @@ -24,16 +22,14 @@ export class PrivacyPolicyContentComponent implements OnInit { * @param loginDataService Login Data * @param translate for the translation */ - public constructor(private loginDataService: LoginDataService, private translate: TranslateService) {} + public constructor(private loginDataService: LoginDataService) {} /** * Subscribes for the privacy policy text */ public ngOnInit(): void { - this.loginDataService.privacy_policy.subscribe(privacyPolicy => { - if (privacyPolicy) { - this.privacyPolicy = this.translate.instant(privacyPolicy); - } + this.loginDataService.privacyPolicy.subscribe(privacyPolicy => { + this.privacyPolicy = privacyPolicy; }); } } diff --git a/client/src/app/site/login/components/login-mask/login-mask.component.ts b/client/src/app/site/login/components/login-mask/login-mask.component.ts index 407dccae0..3b4243462 100644 --- a/client/src/app/site/login/components/login-mask/login-mask.component.ts +++ b/client/src/app/site/login/components/login-mask/login-mask.component.ts @@ -6,18 +6,13 @@ import { Title } from '@angular/platform-browser'; import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; -import { BaseComponent } from 'app/base.component'; +import { BaseViewComponent } from 'app/site/base/base-view'; import { AuthService } from 'app/core/core-services/auth.service'; import { OperatorService } from 'app/core/core-services/operator.service'; -import { environment } from 'environments/environment'; -import { LoginDataService, LoginData } from 'app/core/ui-services/login-data.service'; +import { LoginDataService } from 'app/core/ui-services/login-data.service'; import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher'; -import { HttpService } from 'app/core/core-services/http.service'; import { SpinnerService } from 'app/core/ui-services/spinner.service'; - -interface LoginDataWithInfoText extends LoginData { - info_text?: string; -} +import { MatSnackBar } from '@angular/material'; /** * Login mask component. @@ -29,7 +24,7 @@ interface LoginDataWithInfoText extends LoginData { templateUrl: './login-mask.component.html', styleUrls: ['./login-mask.component.scss'] }) -export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestroy { +export class LoginMaskComponent extends BaseViewComponent implements OnInit, OnDestroy { /** * Show or hide password and change the indicator accordingly */ @@ -72,16 +67,16 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr public constructor( title: Title, translate: TranslateService, + matSnackBar: MatSnackBar, private authService: AuthService, private operator: OperatorService, private router: Router, private route: ActivatedRoute, private formBuilder: FormBuilder, - private httpService: HttpService, private loginDataService: LoginDataService, private spinnerService: SpinnerService ) { - super(title, translate); + super(title, translate, matSnackBar); // Hide the spinner if the user is at `login-mask` spinnerService.setVisibility(false); this.createForm(); @@ -94,17 +89,8 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr * Observes the operator, if a user was already logged in, recreate to user and skip the login */ public ngOnInit(): void { - // Get the login data. Save information to the login data service. If there is an - // error, ignore it. - // TODO: This has to be caught by the offline service - this.httpService.get(environment.urlPrefix + '/users/login/').then( - response => { - if (response.info_text) { - this.installationNotice = response.info_text; - } - this.loginDataService.setLoginData(response); - }, - () => {} + this.subscriptions.push( + this.loginDataService.loginInfoText.subscribe(notice => (this.installationNotice = notice)) ); // Maybe the operator changes and the user is logged in. If so, redirect him and boot OpenSlides. diff --git a/client/src/app/site/login/components/login-wrapper/login-wrapper.component.ts b/client/src/app/site/login/components/login-wrapper/login-wrapper.component.ts index 180b69b94..ccf68f32a 100644 --- a/client/src/app/site/login/components/login-wrapper/login-wrapper.component.ts +++ b/client/src/app/site/login/components/login-wrapper/login-wrapper.component.ts @@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; import { BaseComponent } from '../../../../base.component'; +import { LoginDataService } from 'app/core/ui-services/login-data.service'; /** * Login component. @@ -22,7 +23,11 @@ export class LoginWrapperComponent extends BaseComponent implements OnInit { * @param titleService to set the title * @param translate just needed because super.setTitle depends in the `translator.instant` function */ - public constructor(protected titleService: Title, protected translate: TranslateService) { + public constructor( + protected titleService: Title, + protected translate: TranslateService, + private loginDataService: LoginDataService + ) { super(titleService, translate); } @@ -31,5 +36,6 @@ export class LoginWrapperComponent extends BaseComponent implements OnInit { */ public ngOnInit(): void { super.setTitle('Login'); + this.loginDataService.refresh(); } } diff --git a/client/src/app/site/login/components/reset-password-confirm/reset-password-confirm.component.html b/client/src/app/site/login/components/reset-password-confirm/reset-password-confirm.component.html index d83d9084a..919961543 100644 --- a/client/src/app/site/login/components/reset-password-confirm/reset-password-confirm.component.html +++ b/client/src/app/site/login/components/reset-password-confirm/reset-password-confirm.component.html @@ -21,10 +21,12 @@ color="primary" class="submit-button" [disabled]="newPasswordForm.invalid" - translate > - Reset password + {{ 'Reset password' | translate }} + +   + -
diff --git a/client/src/app/site/login/components/reset-password/reset-password.component.html b/client/src/app/site/login/components/reset-password/reset-password.component.html index 4904bccb7..5f32e70bc 100644 --- a/client/src/app/site/login/components/reset-password/reset-password.component.html +++ b/client/src/app/site/login/components/reset-password/reset-password.component.html @@ -2,7 +2,14 @@

Enter your email to send the password reset link

- + Please enter a valid email address! diff --git a/client/src/app/site/login/components/reset-password/reset-password.component.ts b/client/src/app/site/login/components/reset-password/reset-password.component.ts index 8d1e7122a..33d75da7e 100644 --- a/client/src/app/site/login/components/reset-password/reset-password.component.ts +++ b/client/src/app/site/login/components/reset-password/reset-password.component.ts @@ -8,7 +8,6 @@ import { TranslateService } from '@ngx-translate/core'; import { environment } from 'environments/environment'; import { HttpService } from 'app/core/core-services/http.service'; -import { ThemeService } from 'app/core/ui-services/theme.service'; import { BaseViewComponent } from 'app/site/base/base-view'; /** @@ -35,8 +34,7 @@ export class ResetPasswordComponent extends BaseViewComponent implements OnInit matSnackBar: MatSnackBar, private http: HttpService, formBuilder: FormBuilder, - private router: Router, - private themeService: ThemeService + private router: Router ) { super(titleService, translate, matSnackBar); this.resetPasswordForm = formBuilder.group({ @@ -49,7 +47,6 @@ export class ResetPasswordComponent extends BaseViewComponent implements OnInit */ public ngOnInit(): void { super.setTitle('Reset password'); - this.themeService.checkTheme(); } /** diff --git a/client/src/app/site/site.component.scss b/client/src/app/site/site.component.scss index e3b971e6c..6f47b00dd 100644 --- a/client/src/app/site/site.component.scss +++ b/client/src/app/site/site.component.scss @@ -86,6 +86,7 @@ mat-sidenav-container { height: $size; width: $size; font-size: $size; + margin: 0; } } } diff --git a/openslides/users/views.py b/openslides/users/views.py index 03aeeff8c..bd7287759 100644 --- a/openslides/users/views.py +++ b/openslides/users/views.py @@ -697,20 +697,17 @@ class UserLoginView(WhoAmIDataView): """ if self.request.method == "GET": if config["general_login_info_text"]: - context["info_text"] = config["general_login_info_text"] + context["login_info_text"] = config["general_login_info_text"] else: try: user = User.objects.get(username="admin") - except User.DoesNotExist: - context["info_text"] = "" - else: if user.check_password("admin"): - context["info_text"] = ( + context["login_info_text"] = ( f"Use admin and admin for your first login.
" "Please change your password to hide this message!" ) - else: - context["info_text"] = "" + except User.DoesNotExist: + pass # Add the privacy policy and legal notice, so the client can display it # even, it is not logged in. context["privacy_policy"] = config["general_event_privacy_policy"] diff --git a/tests/integration/users/test_views.py b/tests/integration/users/test_views.py index dcaf0a97f..688a6659a 100644 --- a/tests/integration/users/test_views.py +++ b/tests/integration/users/test_views.py @@ -72,7 +72,7 @@ class TestUserLoginView(TestCase): response = self.client.get(self.url) self.assertEqual(response.status_code, 200) - self.assertTrue(json.loads(response.content.decode()).get("info_text")) + self.assertTrue(json.loads(response.content.decode()).get("login_info_text")) def test_post_no_data(self): response = self.client.post(self.url)