Merge pull request 'Datenschutz und Impressum angeben #251' (#252) from #251 into 1.0.0

Reviewed-on: sh-edraft.de/kd_discord_bot#252
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Closes #251
This commit is contained in:
Sven Heidemann 2023-03-20 13:42:23 +01:00
commit 59162408e5
33 changed files with 1054 additions and 8975 deletions

View File

@ -483,18 +483,19 @@ class AuthService(AuthServiceABC):
if user_dto is None: if user_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, "User not set") raise ServiceException(ServiceErrorCode.InvalidData, "User not set")
members = self._users.get_users_by_discord_id(dc_id)
if members.count() == 0:
raise ServiceException(ServiceErrorCode.InvalidUser, f"Member not found")
added_user = False added_user = False
db_user = self._auth_users.find_auth_user_by_email(user_dto.email) db_user = self._auth_users.find_auth_user_by_email(user_dto.email)
if db_user is None: if db_user is None:
self.add_auth_user(user_dto) self.add_auth_user(user_dto)
added_user = True added_user = True
# raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found')
db_user = self._auth_users.get_auth_user_by_email(user_dto.email) db_user = self._auth_users.get_auth_user_by_email(user_dto.email)
if db_user.users.count() == 0: if db_user.users.count() == 0:
self._users.get_users_by_discord_id(dc_id).for_each( members.for_each(lambda x: self._auth_users.add_auth_user_user_rel(AuthUserUsersRelation(db_user, x)))
lambda x: self._auth_users.add_auth_user_user_rel(AuthUserUsersRelation(db_user, x))
)
if db_user.confirmation_id is not None and not added_user: if db_user.confirmation_id is not None and not added_user:
raise ServiceException(ServiceErrorCode.Forbidden, "E-Mail not verified") raise ServiceException(ServiceErrorCode.Forbidden, "E-Mail not verified")

9562
kdb-web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "kdb-web", "name": "kdb-web",
"version": "1.0.0.rc2", "version": "1.0.dev251",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"update-version": "ts-node-esm update-version.ts", "update-version": "ts-node-esm update-version.ts",

View File

@ -53,9 +53,9 @@ export class AppComponent implements OnInit, OnDestroy {
this.socket.startSocket(); this.socket.startSocket();
} }
ngOnDestroy() { public ngOnDestroy(): void {
this.unsubscriber.next(); this.unsubscriber.next();
this.unsubscriber.unsubscribe(); this.unsubscriber.complete();
} }
loadLang(): void { loadLang(): void {

View File

@ -1,26 +1,28 @@
<footer> <footer>
<div class="left"> <div class="left">
<div class="frontend-version"> <div class="frontend-version">
<span> <span>
{{'footer.frontend' | translate}}: {{'footer.frontend' | translate}}:
</span> </span>
<span> <span>
{{frontendVersion.getVersionString()}} {{frontendVersion.getVersionString()}}
</span> </span>
</div> </div>
<span class="version-divider"> <span class="version-divider">
| |
</span> </span>
<div class="backend-version"> <div class="backend-version">
<span> <span>
{{'footer.backend' | translate}}: {{'footer.backend' | translate}}:
</span> </span>
<span> <span>
{{backendVersion.getVersionString()}} {{backendVersion.getVersionString()}}
</span> </span>
</div>
</div> </div>
<div class="right"> </div>
<a href="https://www.sh-edraft.de/Impressum" target="_blank">{{'footer.imprint' | translate}}</a> <div class="right">
</div> <button pButton label="{{'footer.privacy' | translate}}" class="btn text-btn" (click)="navigateToPrivacy()"></button>
</footer> <span> | </span>
<button pButton label="{{'footer.imprint' | translate}}" class="btn text-btn" (click)="navigateToImprint()"></button>
</div>
</footer>

View File

@ -14,14 +14,17 @@ import { throwError } from "rxjs";
export class FooterComponent implements OnInit { export class FooterComponent implements OnInit {
frontendVersion: SoftwareVersion = new SoftwareVersion("0", "0", "0"); public frontendVersion: SoftwareVersion = new SoftwareVersion("0", "0", "0");
backendVersion: SoftwareVersion = new SoftwareVersion("0", "0", "0"); public backendVersion: SoftwareVersion = new SoftwareVersion("0", "0", "0");
public privacy: string = "";
public imprint: string = "";
constructor( constructor(
private settings: SettingsService, private settings: SettingsService,
private guiService: GuiService, private guiService: GuiService,
private spinnerService: SpinnerService private spinnerService: SpinnerService
) { } ) {}
ngOnInit(): void { ngOnInit(): void {
this.frontendVersion = this.settings.getWebVersion() ?? new SoftwareVersion('0', '0', '0'); this.frontendVersion = this.settings.getWebVersion() ?? new SoftwareVersion('0', '0', '0');
@ -42,4 +45,12 @@ export class FooterComponent implements OnInit {
}); });
} }
public navigateToPrivacy(): void {
window.open(this.settings.getPrivacyURL(), "_blank");
}
public navigateToImprint(): void {
window.open(this.settings.getImprintURL(), "_blank");
}
} }

View File

@ -3,6 +3,8 @@ import { Theme } from '../view/theme';
export interface Appsettings { export interface Appsettings {
ApiURL: string; ApiURL: string;
PrivacyURL: string;
ImprintURL: string;
WebVersion: SoftwareVersion; WebVersion: SoftwareVersion;
Themes: Theme[]; Themes: Theme[];
} }

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { catchError, debounceTime, takeUntil } from "rxjs/operators"; import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { AuthRoles } from "src/app/models/auth/auth-roles.enum"; import { AuthRoles } from "src/app/models/auth/auth-roles.enum";
import { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; import { AuthUserDTO } from "src/app/models/auth/auth-user.dto";
@ -22,7 +22,7 @@ import { TranslateService } from "@ngx-translate/core";
templateUrl: "./auth-user.component.html", templateUrl: "./auth-user.component.html",
styleUrls: ["./auth-user.component.scss"] styleUrls: ["./auth-user.component.scss"]
}) })
export class AuthUserComponent implements OnInit { export class AuthUserComponent implements OnInit, OnDestroy {
users!: AuthUserDTO[]; users!: AuthUserDTO[];
statuses!: any[]; statuses!: any[];
@ -62,7 +62,7 @@ export class AuthUserComponent implements OnInit {
searchCriterions!: AuthUserSelectCriterion; searchCriterions!: AuthUserSelectCriterion;
totalRecords!: number; totalRecords!: number;
private unsubscriber = new Subject(); private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
@ -91,6 +91,11 @@ export class AuthUserComponent implements OnInit {
this.loadNextPage(); this.loadNextPage();
} }
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
setFilterForm() { setFilterForm() {
this.filterForm = this.fb.group({ this.filterForm = this.fb.group({
firstName: [""], firstName: [""],

View File

@ -1,15 +1,14 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from "@ngx-translate/core";
import { catchError } from 'rxjs/operators'; import { catchError } from "rxjs/operators";
import { SettingsDTO } from 'src/app/models/config/settings.dto'; import { SettingsDTO } from "src/app/models/config/settings.dto";
import { ErrorDTO } from 'src/app/models/error/error-dto'; import { ErrorDTO } from "src/app/models/error/error-dto";
import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum";
import { AuthService } from 'src/app/services/auth/auth.service'; import { GuiService } from "src/app/services/gui/gui.service";
import { GuiService } from 'src/app/services/gui/gui.service'; import { SettingsService } from "src/app/services/settings/settings.service";
import { SettingsService } from 'src/app/services/settings/settings.service'; import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { SpinnerService } from 'src/app/services/spinner/spinner.service'; import { ToastService } from "src/app/services/toast/toast.service";
import { ToastService } from 'src/app/services/toast/toast.service';
import { throwError } from "rxjs"; import { throwError } from "rxjs";
@Component({ @Component({

View File

@ -7,6 +7,7 @@ import { AuthHeaderComponent } from './components/auth-header/auth-header.compon
import { ForgetPasswordComponent } from './components/forget-password/forget-password.component'; import { ForgetPasswordComponent } from './components/forget-password/forget-password.component';
import { LoginComponent } from './components/login/login.component'; import { LoginComponent } from './components/login/login.component';
import { RegistrationComponent } from './components/registration/registration.component'; import { RegistrationComponent } from './components/registration/registration.component';
import { AuthFooterComponent } from "./components/auth-footer/auth-footer.component";
@NgModule({ @NgModule({
@ -14,7 +15,8 @@ import { RegistrationComponent } from './components/registration/registration.co
ForgetPasswordComponent, ForgetPasswordComponent,
LoginComponent, LoginComponent,
RegistrationComponent, RegistrationComponent,
AuthHeaderComponent AuthHeaderComponent,
AuthFooterComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

View File

@ -0,0 +1,7 @@
<div class="auth-footer">
<div class="logo-button-wrapper">
<button pButton label="{{'footer.privacy' | translate}}" class="btn text-btn" (click)="navigateToPrivacy()"></button>
<span> | </span>
<button pButton label="{{'footer.imprint' | translate}}" class="btn text-btn" (click)="navigateToImprint()"></button>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AuthFooterComponent } from './auth-footer.component';
describe('AuthFooterComponent', () => {
let component: AuthFooterComponent;
let fixture: ComponentFixture<AuthFooterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AuthFooterComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AuthFooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,32 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { SettingsService } from "../../../../services/settings/settings.service";
@Component({
selector: "app-auth-footer",
templateUrl: "./auth-footer.component.html",
styleUrls: ["./auth-footer.component.scss"]
})
export class AuthFooterComponent implements OnInit {
private privacy: string = "";
private imprint: string = "";
constructor(
private settings: SettingsService
) {
}
ngOnInit(): void {
this.privacy = this.settings.getPrivacyURL();
this.imprint = this.settings.getImprintURL();
}
public navigateToPrivacy(): void {
window.open(this.settings.getPrivacyURL(), "_blank");
}
public navigateToImprint(): void {
window.open(this.settings.getImprintURL(), "_blank");
}
}

View File

@ -1,10 +1,10 @@
<div class="auth-header"> <div class="auth-header">
<div class="logo-button-wrapper"> <div class="logo-button-wrapper">
<button type="button" pButton icon="pi pi-globe" class="btn icon-btn p-button-text" (click)="authLangMenu.toggle($event)"></button> <button type="button" pButton icon="pi pi-globe" class="btn icon-btn p-button-text" (click)="authLangMenu.toggle($event)"></button>
<p-menu #authLangMenu [popup]="true" [model]="langList" class="lang-menu"></p-menu> <p-menu #authLangMenu [popup]="true" [model]="langList" class="lang-menu"></p-menu>
</div> </div>
<div class="logo-button-wrapper"> <div class="logo-button-wrapper">
<button type="button" pButton icon="pi pi-palette" class="btn icon-btn p-button-text" (click)="authThemeMenu.toggle($event)"></button> <button type="button" pButton icon="pi pi-palette" class="btn icon-btn p-button-text" (click)="authThemeMenu.toggle($event)"></button>
<p-menu #authThemeMenu [popup]="true" [model]="themeList" class="theme-menu"></p-menu> <p-menu #authThemeMenu [popup]="true" [model]="themeList" class="theme-menu"></p-menu>
</div> </div>
</div> </div>

View File

@ -1,12 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from "@angular/core";
import { Router } from '@angular/router'; import { Router } from "@angular/router";
import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; import { TranslateService } from "@ngx-translate/core";
import { MenuItem, PrimeNGConfig } from 'primeng/api'; import { MenuItem, PrimeNGConfig } from "primeng/api";
import { catchError } from 'rxjs'; import { SettingsService } from "src/app/services/settings/settings.service";
import { AuthService } from 'src/app/services/auth/auth.service'; import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { SettingsService } from 'src/app/services/settings/settings.service'; import { ThemeService } from "src/app/services/theme/theme.service";
import { SpinnerService } from 'src/app/services/spinner/spinner.service';
import { ThemeService } from 'src/app/services/theme/theme.service';
@Component({ @Component({
selector: 'app-auth-header', selector: 'app-auth-header',
@ -14,9 +12,8 @@ import { ThemeService } from 'src/app/services/theme/theme.service';
styleUrls: ['./auth-header.component.scss'] styleUrls: ['./auth-header.component.scss']
}) })
export class AuthHeaderComponent implements OnInit { export class AuthHeaderComponent implements OnInit {
langList: MenuItem[] = []; public langList: MenuItem[] = [];
themeList: MenuItem[] = []; public themeList: MenuItem[] = [];
userMenuList!: MenuItem[];
constructor( constructor(
private router: Router, private router: Router,
@ -27,12 +24,12 @@ export class AuthHeaderComponent implements OnInit {
private config: PrimeNGConfig private config: PrimeNGConfig
) { } ) { }
ngOnInit(): void { public ngOnInit(): void {
this.initMenuLists(); this.initMenuLists();
this.loadLang(); this.loadLang();
} }
initMenuLists(): void { private initMenuLists(): void {
this.langList = [ this.langList = [
{ {
label: 'English', command: () => { label: 'English', command: () => {
@ -58,16 +55,16 @@ export class AuthHeaderComponent implements OnInit {
}); });
} }
changeTheme(name: string): void { private changeTheme(name: string): void {
this.themeService.setTheme(name, true); this.themeService.setTheme(name, true);
} }
translate(lang: string) { private translate(lang: string) {
this.translateService.use(lang); this.translateService.use(lang);
this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res)); this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res));
} }
loadLang(): void { private loadLang(): void {
let lang = localStorage.getItem(`default_lang`); let lang = localStorage.getItem(`default_lang`);
if (!lang) { if (!lang) {
lang = 'en'; lang = 'en';
@ -76,7 +73,7 @@ export class AuthHeaderComponent implements OnInit {
this.translate(lang); this.translate(lang);
} }
setLang(lang: string): void { private setLang(lang: string): void {
localStorage.setItem(`default_lang`, lang); localStorage.setItem(`default_lang`, lang);
} }

View File

@ -1,63 +1,64 @@
<section class="login-wrapper"> <section class="login-wrapper">
<div class="login-form-wrapper"> <div class="login-form-wrapper">
<div class="login-form"> <div class="login-form">
<ng-container *ngIf="resetPasswordId === null; else resetPasswordForm"> <ng-container *ngIf="resetPasswordId === null; else resetPasswordForm">
<form [formGroup]="emailForm"> <form [formGroup]="emailForm">
<h1>{{'auth.header' | translate}}</h1> <h1>{{'auth.header' | translate}}</h1>
<div *ngIf="!ready" class="input-field"> <div *ngIf="!ready" class="input-field">
<input type="email" pInputText formControlName="email" <input type="email" pInputText formControlName="email"
placeholder="{{'common.email' | translate}}" autocomplete="username email"> placeholder="{{'common.email' | translate}}" autocomplete="username email">
</div> </div>
<div *ngIf="ready" class="input-field-info-text"> <div *ngIf="ready" class="input-field-info-text">
{{'auth.forgot_password.send_confirmation_url' | translate}} {{'auth.forgot_password.send_confirmation_url' | translate}}
</div> </div>
<div class="login-form-submit"> <div class="login-form-submit">
<button pButton label="{{'auth.forgot_password.reset_password' | translate}}" <button pButton label="{{'auth.forgot_password.reset_password' | translate}}"
class="btn login-form-submit-btn" (click)="forgotPassword()" class="btn login-form-submit-btn" (click)="forgotPassword()"
[disabled]="emailForm.invalid || ready"></button> [disabled]="emailForm.invalid || ready"></button>
</div> </div>
<div class="login-form-sub-button-wrapper"> <div class="login-form-sub-button-wrapper">
<div class="login-form-sub-btn-wrapper"> <div class="login-form-sub-btn-wrapper">
<button pButton label="{{'auth.forgot_password.login' | translate}}" <button pButton label="{{'auth.forgot_password.login' | translate}}"
class="btn login-form-sub-btn" (click)="login()"></button> class="btn login-form-sub-btn" (click)="login()"></button>
</div> </div>
<div class="login-form-sub-btn-wrapper"> <div class="login-form-sub-btn-wrapper">
<button pButton label="{{'auth.forgot_password.register' | translate}}" <button pButton label="{{'auth.forgot_password.register' | translate}}"
class="btn login-form-sub-btn" (click)="register()"></button> class="btn login-form-sub-btn" (click)="register()"></button>
</div> </div>
</div> </div>
</form> </form>
</ng-container> </ng-container>
<ng-template #resetPasswordForm> <ng-template #resetPasswordForm>
<form [formGroup]="passwordForm"> <form [formGroup]="passwordForm">
<h1>{{'auth.header' | translate}}</h1> <h1>{{'auth.header' | translate}}</h1>
<div class="input-field"> <div class="input-field">
<input type="password" pInputText formControlName="password" <input type="password" pInputText formControlName="password"
placeholder="{{'auth.forgot_password.password' | translate}}" autocomplete="new-password"> placeholder="{{'auth.forgot_password.password' | translate}}" autocomplete="new-password">
</div> </div>
<div class="input-field"> <div class="input-field">
<input type="password" pInputText formControlName="passwordRepeat" <input type="password" pInputText formControlName="passwordRepeat"
placeholder="{{'auth.forgot_password.repeat_password' | translate}}" placeholder="{{'auth.forgot_password.repeat_password' | translate}}"
[ngClass]="{ 'invalid-feedback-input': submitted && repeatErrors.password}"> [ngClass]="{ 'invalid-feedback-input': submitted && repeatErrors.password}">
<div *ngIf="submitted" class="invalid-feedback"> <div *ngIf="submitted" class="invalid-feedback">
<div *ngIf="repeatErrors.password">{{'auth.forgot_password.passwords_do_not_match' | <div *ngIf="repeatErrors.password">{{'auth.forgot_password.passwords_do_not_match' |
translate}}</div> translate}}</div>
</div> </div>
</div> </div>
<div class="login-form-submit"> <div class="login-form-submit">
<button pButton label="{{'auth.forgot_password.reset_password' | translate}}" <button pButton label="{{'auth.forgot_password.reset_password' | translate}}"
class="btn login-form-submit-btn" (click)="resetPassword()" class="btn login-form-submit-btn" (click)="resetPassword()"
[disabled]="passwordForm.invalid || ready"></button> [disabled]="passwordForm.invalid || ready"></button>
</div> </div>
</form> </form>
</ng-template> </ng-template>
</div>
</div> </div>
</div>
<app-auth-header></app-auth-header> <app-auth-header></app-auth-header>
<app-auth-footer></app-auth-footer>
</section> </section>

View File

@ -62,4 +62,5 @@
</div> </div>
<app-auth-header></app-auth-header> <app-auth-header></app-auth-header>
<app-auth-footer></app-auth-footer>
</section> </section>

View File

@ -67,11 +67,16 @@
</div> </div>
</div> </div>
<div class="input-field">
<p-checkbox id="confirmPrivacyCheckbox" [binary]="true" formControlName="confirmPrivacy"></p-checkbox>
&nbsp;
<label for="confirmPrivacyCheckbox" [innerHTML]="confirmPrivacyString"></label>
</div>
<div class="login-form-submit"> <div class="login-form-submit">
<button pButton label="{{'auth.register.register' | translate}}" class="btn login-form-submit-btn" (click)="register()" <button pButton label="{{'auth.register.register' | translate}}" class="btn login-form-submit-btn" (click)="register()"
[disabled]="loginForm.invalid"></button> [disabled]="loginForm.invalid"></button>
</div> </div>
<div class="login-form-sub-button-wrapper"> <div class="login-form-sub-button-wrapper">
<div class="login-form-sub-btn-wrapper"> <div class="login-form-sub-btn-wrapper">
<button pButton label="{{'auth.register.login' | translate}}" class="btn login-form-sub-btn" (click)="login()"></button> <button pButton label="{{'auth.register.login' | translate}}" class="btn login-form-sub-btn" (click)="login()"></button>
@ -82,4 +87,5 @@
</div> </div>
<app-auth-header></app-auth-header> <app-auth-header></app-auth-header>
<app-auth-footer></app-auth-footer>
</section> </section>

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { catchError, finalize } from "rxjs/operators"; import { catchError, takeUntil } from "rxjs/operators";
import { AuthErrorMessages } from "src/app/models/auth/auth-error-messages.enum"; import { AuthErrorMessages } from "src/app/models/auth/auth-error-messages.enum";
import { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; import { AuthUserDTO } from "src/app/models/auth/auth-user.dto";
import { AuthUserAtrErrors } from "src/app/models/auth/auth-user-atr-errors"; import { AuthUserAtrErrors } from "src/app/models/auth/auth-user-atr-errors";
@ -9,15 +9,16 @@ import { ErrorDTO } from "src/app/models/error/error-dto";
import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum"; import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum";
import { AuthService } from "src/app/services/auth/auth.service"; import { AuthService } from "src/app/services/auth/auth.service";
import { SpinnerService } from "src/app/services/spinner/spinner.service"; import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { throwError } from "rxjs"; import { Subject, throwError } from "rxjs";
import { OAuthDTO } from "../../../../models/auth/oauth.dto"; import { TranslateService } from "@ngx-translate/core";
import { SettingsService } from "../../../../services/settings/settings.service";
@Component({ @Component({
selector: "app-registration", selector: "app-registration",
templateUrl: "./registration.component.html", templateUrl: "./registration.component.html",
styleUrls: ["./registration.component.scss"] styleUrls: ["./registration.component.scss"]
}) })
export class RegistrationComponent implements OnInit { export class RegistrationComponent implements OnInit, OnDestroy {
loginForm!: FormGroup<{ loginForm!: FormGroup<{
firstName: FormControl<string | null>, firstName: FormControl<string | null>,
@ -25,7 +26,8 @@ export class RegistrationComponent implements OnInit {
email: FormControl<string | null>, email: FormControl<string | null>,
emailRepeat: FormControl<string | null>, emailRepeat: FormControl<string | null>,
password: FormControl<string | null>, password: FormControl<string | null>,
passwordRepeat: FormControl<string | null> passwordRepeat: FormControl<string | null>,
confirmPrivacy: FormControl<boolean | null>
}>; }>;
submitted = false; submitted = false;
authUserAtrErrors!: AuthUserAtrErrors; authUserAtrErrors!: AuthUserAtrErrors;
@ -34,12 +36,17 @@ export class RegistrationComponent implements OnInit {
password: false password: false
}; };
public confirmPrivacyString: string = "";
private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private router: Router, private router: Router,
private spinnerService: SpinnerService, private spinnerService: SpinnerService,
private route: ActivatedRoute private route: ActivatedRoute,
private translate: TranslateService,
private settings: SettingsService
) { ) {
this.spinnerService.showSpinner(); this.spinnerService.showSpinner();
this.authService.isUserLoggedInAsync().then(res => { this.authService.isUserLoggedInAsync().then(res => {
@ -51,12 +58,22 @@ export class RegistrationComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.translate.onLangChange.pipe(takeUntil(this.unsubscriber)).subscribe(lang => {
this.confirmPrivacyString = this.translate.instant("auth.register.confirm_privacy", { url: this.settings.getPrivacyURL() });
});
this.confirmEMail(); this.confirmEMail();
this.initData();
this.spinnerService.showSpinner(); this.spinnerService.showSpinner();
this.initData(); this.initData();
} }
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
initData() { initData() {
this.initLoginForm(); this.initLoginForm();
this.resetStateFlags(); this.resetStateFlags();
@ -82,7 +99,8 @@ export class RegistrationComponent implements OnInit {
email: ["", [Validators.required, Validators.email]], email: ["", [Validators.required, Validators.email]],
emailRepeat: ["", [Validators.required, Validators.email]], emailRepeat: ["", [Validators.required, Validators.email]],
password: ["", [Validators.required, Validators.minLength(8)]], password: ["", [Validators.required, Validators.minLength(8)]],
passwordRepeat: ["", [Validators.required, Validators.minLength(8)]] passwordRepeat: ["", [Validators.required, Validators.minLength(8)]],
confirmPrivacy: [false, [Validators.required, Validators.requiredTrue]]
}); });
} }
@ -143,7 +161,7 @@ export class RegistrationComponent implements OnInit {
.pipe(catchError(error => { .pipe(catchError(error => {
this.router.navigate(["/auth/login"]); this.router.navigate(["/auth/login"]);
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
return throwError(() => error); return throwError(() => error);
})) }))
.subscribe(resp => { .subscribe(resp => {
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
@ -151,4 +169,8 @@ export class RegistrationComponent implements OnInit {
}); });
} }
} }
log($event: Event): void {
console.log($event);
}
} }

View File

@ -60,7 +60,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
this.loadNextPage(); this.loadNextPage();
} }
public ngOnDestroy() { public ngOnDestroy(): void {
this.unsubscriber.next(); this.unsubscriber.next();
this.unsubscriber.complete(); this.unsubscriber.complete();
} }

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { DataService } from "../../../../../../services/data/data.service"; import { DataService } from "../../../../../../services/data/data.service";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { AutoRoleRule, AutoRoleRuleFilter } from "../../../../../../models/data/auto_role.model"; import { AutoRoleRule, AutoRoleRuleFilter } from "../../../../../../models/data/auto_role.model";
@ -28,7 +28,7 @@ import { Subject, throwError } from "rxjs";
templateUrl: "./auto-roles-rules.component.html", templateUrl: "./auto-roles-rules.component.html",
styleUrls: ["./auto-roles-rules.component.scss"] styleUrls: ["./auto-roles-rules.component.scss"]
}) })
export class AutoRolesRulesComponent implements OnInit { export class AutoRolesRulesComponent implements OnInit, OnDestroy {
rules: AutoRoleRule[] = []; rules: AutoRoleRule[] = [];
guild: Guild = { channels: [], emojis: [], roles: [] }; guild: Guild = { channels: [], emojis: [], roles: [] };
@ -85,7 +85,6 @@ export class AutoRolesRulesComponent implements OnInit {
} }
public ngOnInit(): void { public ngOnInit(): void {
this.setFilterForm(); this.setFilterForm();
this.data.getServerFromRoute(this.route).then(server => { this.data.getServerFromRoute(this.route).then(server => {
this.server = server; this.server = server;
@ -119,6 +118,11 @@ export class AutoRolesRulesComponent implements OnInit {
}); });
} }
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
public loadNextPage(): void { public loadNextPage(): void {
this.loading = true; this.loading = true;
this.data.query<AutoRoleRuleQuery>(Queries.autoRoleRulesQuery, { this.data.query<AutoRoleRuleQuery>(Queries.autoRoleRulesQuery, {

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { User } from "../../../../../../models/data/user.model"; import { User } from "../../../../../../models/data/user.model";
import { LazyLoadEvent, MenuItem } from "primeng/api"; import { LazyLoadEvent, MenuItem } from "primeng/api";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
@ -28,7 +28,7 @@ import { Server } from "../../../../../../models/data/server.model";
templateUrl: "./auto-roles.component.html", templateUrl: "./auto-roles.component.html",
styleUrls: ["./auto-roles.component.scss"] styleUrls: ["./auto-roles.component.scss"]
}) })
export class AutoRolesComponent implements OnInit { export class AutoRolesComponent implements OnInit, OnDestroy {
auto_roles: AutoRole[] = []; auto_roles: AutoRole[] = [];
guild: Guild = { channels: [], emojis: [], roles: [] }; guild: Guild = { channels: [], emojis: [], roles: [] };
channels: MenuItem[] = []; channels: MenuItem[] = [];
@ -100,6 +100,12 @@ export class AutoRolesComponent implements OnInit {
}); });
} }
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
public loadNextPage(): void { public loadNextPage(): void {
this.loading = true; this.loading = true;
this.data.query<AutoRoleQuery>(Queries.autoRolesQuery, { this.data.query<AutoRoleQuery>(Queries.autoRolesQuery, {

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
import { AuthService } from "../../../../services/auth/auth.service"; import { AuthService } from "../../../../services/auth/auth.service";
import { SpinnerService } from "../../../../services/spinner/spinner.service"; import { SpinnerService } from "../../../../services/spinner/spinner.service";
@ -26,7 +26,7 @@ import { Server } from "../../../../models/data/server.model";
templateUrl: "./members.component.html", templateUrl: "./members.component.html",
styleUrls: ["./members.component.scss"] styleUrls: ["./members.component.scss"]
}) })
export class MembersComponent implements OnInit { export class MembersComponent implements OnInit, OnDestroy {
members!: User[]; members!: User[];
levels!: MenuItem[]; levels!: MenuItem[];
leftServerOptions = [ leftServerOptions = [
@ -114,6 +114,10 @@ export class MembersComponent implements OnInit {
this.loadNextPage(); this.loadNextPage();
}); });
} }
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
loadNextPage() { loadNextPage() {
this.spinner.showSpinner(); this.spinner.showSpinner();

View File

@ -30,13 +30,29 @@ export class SettingsService {
} }
public getApiURL(): string { public getApiURL(): string {
if (!this.appsettings || !this.appsettings.Themes) { if (!this.appsettings || !this.appsettings.ApiURL) {
console.error('ApiUrl is not set!'); console.error('ApiUrl is not set!');
return ""; return "";
} }
return this.appsettings.ApiURL; return this.appsettings.ApiURL;
} }
public getPrivacyURL(): string {
if (!this.appsettings || !this.appsettings.PrivacyURL) {
console.error('PrivacyURL is not set!');
return "";
}
return this.appsettings.PrivacyURL;
}
public getImprintURL(): string {
if (!this.appsettings || !this.appsettings.ImprintURL) {
console.error('ImprintURL is not set!');
return "";
}
return this.appsettings.ImprintURL;
}
public getWebVersion(): SoftwareVersion | null { public getWebVersion(): SoftwareVersion | null {
if (!this.appsettings || !this.appsettings.WebVersion) { if (!this.appsettings || !this.appsettings.WebVersion) {
console.error('WebVersion is not set!'); console.error('WebVersion is not set!');

View File

@ -1,9 +1,11 @@
{ {
"ApiURL": "http://localhost:8044", "ApiURL": "http://localhost:8044",
"PrivacyURL": "https://www.sh-edraft.de/Datenschutz",
"ImprintURL": "https://www.sh-edraft.de/Impressum",
"WebVersion": { "WebVersion": {
"Major": "1", "Major": "1",
"Minor": "0", "Minor": "0",
"Micro": "0.rc2" "Micro": "dev251"
}, },
"Themes": [ "Themes": [
{ {

View File

@ -113,7 +113,8 @@
"register_with_discord": "Mit Discord Registrieren", "register_with_discord": "Mit Discord Registrieren",
"repeat_email": "E-Mail wiederholen", "repeat_email": "E-Mail wiederholen",
"repeat_password": "Passwort wiederholen", "repeat_password": "Passwort wiederholen",
"user_already_exists": "Benutzer existiert bereits" "user_already_exists": "Benutzer existiert bereits",
"confirm_privacy": "Ich erkläre mich mit der <a href=\"{{url}}\">Datenschutzerklärung</a> einverstanden."
} }
}, },
"common": { "common": {
@ -163,7 +164,8 @@
"footer": { "footer": {
"backend": "API", "backend": "API",
"frontend": "Webseite", "frontend": "Webseite",
"imprint": "Impressum" "imprint": "Impressum",
"privacy": "Datenschutz"
}, },
"general": { "general": {
"days": "Tage", "days": "Tage",

View File

@ -113,7 +113,8 @@
"register_with_discord": "Register with discord", "register_with_discord": "Register with discord",
"repeat_email": "Repeat E-mail", "repeat_email": "Repeat E-mail",
"repeat_password": "Repeat password", "repeat_password": "Repeat password",
"user_already_exists": "User already exists" "user_already_exists": "User already exists",
"confirm_privacy": "I agree to the <a href=\"{{url}}\">Privacy Policy</a>."
} }
}, },
"common": { "common": {
@ -163,7 +164,8 @@
"footer": { "footer": {
"backend": "Website", "backend": "Website",
"frontend": "API", "frontend": "API",
"imprint": "Imprint" "imprint": "Imprint",
"privacy": "Privacy"
}, },
"general": { "general": {
"days": "Days", "days": "Days",

View File

@ -100,6 +100,9 @@ header {
} }
} }
.auth-footer {
}
.app { .app {
height: 100%; height: 100%;
display: flex; display: flex;
@ -474,6 +477,9 @@ footer {
.right { .right {
width: 50%; width: 50%;
text-align: right; text-align: right;
.p-button-label {
font-weight: unset !important;
}
} }
} }
@ -488,7 +494,8 @@ footer {
gap: 15px; gap: 15px;
.login-form-wrapper, .login-form-wrapper,
.auth-header { .auth-header,
.auth-footer {
width: 350px; width: 350px;
height: 450px; height: 450px;
@ -549,6 +556,11 @@ footer {
width: 350px; width: 350px;
height: 75px; height: 75px;
} }
.auth-footer {
width: 350px;
height: 75px;
}
} }
.input-field { .input-field {

View File

@ -231,8 +231,10 @@
background-color: $secondaryBackgroundColor; background-color: $secondaryBackgroundColor;
.login-form-wrapper, .login-form-wrapper,
.auth-header { .auth-header,
.auth-footer {
background-color: $primaryBackgroundColor; background-color: $primaryBackgroundColor;
color: $primaryTextColor !important;
.login-form { .login-form {
.input-field { .input-field {

View File

@ -231,8 +231,10 @@
background-color: $secondaryBackgroundColor; background-color: $secondaryBackgroundColor;
.login-form-wrapper, .login-form-wrapper,
.auth-header { .auth-header,
.auth-footer {
background-color: $primaryBackgroundColor; background-color: $primaryBackgroundColor;
color: $primaryTextColor !important;
.login-form { .login-form {
.input-field { .input-field {

View File

@ -235,8 +235,10 @@
background-color: $secondaryBackgroundColor; background-color: $secondaryBackgroundColor;
.login-form-wrapper, .login-form-wrapper,
.auth-header { .auth-header,
.auth-footer {
background-color: $primaryBackgroundColor; background-color: $primaryBackgroundColor;
color: $primaryTextColor !important;
.login-form { .login-form {
.input-field { .input-field {

View File

@ -231,8 +231,10 @@
background-color: $secondaryBackgroundColor; background-color: $secondaryBackgroundColor;
.login-form-wrapper, .login-form-wrapper,
.auth-header { .auth-header,
.auth-footer {
background-color: $primaryBackgroundColor; background-color: $primaryBackgroundColor;
color: $primaryTextColor !important;
.login-form { .login-form {
.input-field { .input-field {