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
This commit is contained in:
GabrielMeyer 2019-07-26 11:47:04 +02:00 committed by Sean Engelhardt
parent 20cbe37d74
commit b5b3e60e81
30 changed files with 294 additions and 193 deletions

View File

@ -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();
});
}));
});

View File

@ -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]
});
});

View File

@ -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();
});
}));
});

View File

@ -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();
}));
});

View File

@ -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();
}));
});

View File

@ -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]
});
});

View File

@ -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<string>('');
/**
* Returns the installation notice as observable.
*/
public get loginInfoText(): Observable<string> {
return this._loginInfoText.asObservable();
}
/**
* Holds the privacy policy
*/
private readonly _privacy_policy = new BehaviorSubject<string>('');
private readonly _privacyPolicy = new BehaviorSubject<string>('');
/**
* Returns an observable for the privacy policy
*/
public get privacy_policy(): Observable<string> {
return this._privacy_policy.asObservable();
public get privacyPolicy(): Observable<string> {
return this._privacyPolicy.asObservable();
}
/**
* Holds the legal notice
*/
private readonly _legal_notice = new BehaviorSubject<string>('');
private readonly _legalNotice = new BehaviorSubject<string>('');
/**
* Returns an observable for the legal notice
*/
public get legal_notice(): Observable<string> {
return this._legal_notice.asObservable();
public get legalNotice(): Observable<string> {
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<void>();
/**
* 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<void> {
await this.loadLoginData();
this.configService.get<string>('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<string>('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<string>('openslides_theme').subscribe(value => {
this._theme.next(value);
this.storeLoginData();
this.configService.get<string>('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<void> {
try {
const loginData = await this.httpService.get<LoginData>(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<void> {
const loginData = await this.storageService.get<LoginData | null>(LOGIN_DATA_STORAGE_KEY);
if (loginData) {
const loginData = await this.storageService.get<any>(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);
}
}

View File

@ -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();
}));
});

View File

@ -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();
}));
});

View File

@ -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();
}));
});

View File

@ -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();
}));
});

View File

@ -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();
}));
});

View File

@ -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();
}));
});

View File

@ -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);
}
}
}

View File

@ -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();
});
}));
});

View File

@ -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]
});
});

View File

@ -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();
}));
});

View File

@ -13,6 +13,7 @@
.toolbar-left {
display: flex;
max-width: calc(100% - 100px);
button {
margin: 12px 0;
}

View File

@ -1,6 +1,9 @@
<mat-card class="os-card">
<div>
<div class="legal-notice-text" [innerHtml]="legalNotice"></div>
<div *ngIf="legalNotice" class="legal-notice-text" [innerHtml]="legalNotice"></div>
<div *ngIf="!legalNotice" translate>
The event manager hasn't set up a legal notice yet.
</div>
<mat-divider></mat-divider>
<div *ngIf="versionInfo" class="version-text">
<a [attr.href]="versionInfo.openslides_url" target="_blank">

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
});
}
}

View File

@ -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<LoginDataWithInfoText>(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.

View File

@ -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();
}
}

View File

@ -21,10 +21,12 @@
color="primary"
class="submit-button"
[disabled]="newPasswordForm.invalid"
translate
>
Reset password
{{ 'Reset password' | translate }}
</button>
&nbsp;
<button type="button" mat-button routerLink="/login">
{{ 'Back to login' | translate }}
</button>
<button type="button" class="back-button" routerLink="/login" translate>Back to login</button>
</form>
</div>

View File

@ -2,7 +2,14 @@
<form [formGroup]="resetPasswordForm" (ngSubmit)="resetPassword()" autocomplete="off">
<h3 translate>Enter your email to send the password reset link</h3>
<mat-form-field>
<input matInput required placeholder="{{ 'Email' | translate }}" formControlName="email" type="email" autocomplete="off" />
<input
matInput
required
placeholder="{{ 'Email' | translate }}"
formControlName="email"
type="email"
autocomplete="off"
/>
<mat-error *ngIf="resetPasswordForm.get('email').invalid" translate>
Please enter a valid email address!
</mat-error>

View File

@ -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();
}
/**

View File

@ -86,6 +86,7 @@ mat-sidenav-container {
height: $size;
width: $size;
font-size: $size;
margin: 0;
}
}
}

View File

@ -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 <strong>admin</strong> and <strong>admin</strong> for your first login.<br>"
"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"]

View File

@ -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)