diff --git a/client/proxy.conf.json b/client/proxy.conf.json index ef96b287f..039bc5869 100644 --- a/client/proxy.conf.json +++ b/client/proxy.conf.json @@ -1,4 +1,8 @@ { + "/apps/core/version": { + "target": "http://localhost:8000", + "secure": false + }, "/apps/users/login": { "target": "http://localhost:8000", "secure": false diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 1d9cd898a..3ab3739b0 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -1,12 +1,25 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { LoginComponent } from './site/login/login.component'; + +import { LoginComponent } from './site/login/components/login-wrapper/login.component'; +import { LoginMaskComponent } from './site/login/components/login-mask/login-mask.component'; +import { LoginLegalNoticeComponent } from './site/login/components/login-legal-notice/login-legal-notice.component'; +import { LoginPrivacyPolicyComponent } from './site/login/components/login-privacy-policy/login-privacy-policy.component'; /** * Global app routing */ const routes: Routes = [ - { path: 'login', component: LoginComponent }, + { + path: 'login', + component: LoginComponent, + children: [ + { path: '', component: LoginMaskComponent }, + { path: 'legalnotice', component: LoginLegalNoticeComponent }, + { path: 'privacypolicy', component: LoginPrivacyPolicyComponent } + ] + }, + { path: 'projector', loadChildren: './projector-container/projector-container.module#ProjectorContainerModule' }, { path: '', loadChildren: './site/site.module#SiteModule' }, { path: '**', redirectTo: '' } diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index cc1a96c9d..c9f729762 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -5,6 +5,8 @@ import { Subject } from 'rxjs'; import { AppModule } from './app.module'; import { OpenSlidesComponent } from './openslides.component'; import { OpenSlidesService } from './core/services/openslides.service'; +import { LoginDataService } from './core/services/login-data.service'; +import { ConfigService } from './core/services/config.service'; /** * Angular's global App Component @@ -38,7 +40,9 @@ export class AppComponent { public constructor( translate: TranslateService, private operator: OperatorService, - private OpenSlides: OpenSlidesService + private OpenSlides: OpenSlidesService, + private configService: ConfigService, + private loginDataService: LoginDataService ) { // manually add the supported languages translate.addLangs(['en', 'de', 'fr']); @@ -61,6 +65,8 @@ export class AppComponent { // Setup the operator after the root injector is known. this.operator.setupSubscription(); + this.configService.setupSubscription(); + this.loginDataService.setupSubscription(); this.OpenSlides.bootup(); // Yeah! } diff --git a/client/src/app/core/services/config.service.ts b/client/src/app/core/services/config.service.ts index 0319ec4b7..da32623d2 100644 --- a/client/src/app/core/services/config.service.ts +++ b/client/src/app/core/services/config.service.ts @@ -33,7 +33,9 @@ export class ConfigService extends OpenSlidesComponent { */ public constructor() { super(); + } + public setupSubscription(): void { this.DS.changeObservable.subscribe(data => { // on changes notify the observers for specific keys. if (data instanceof Config && this.configSubjects[data.key]) { diff --git a/client/src/app/core/services/login-data.service.spec.ts b/client/src/app/core/services/login-data.service.spec.ts new file mode 100644 index 000000000..7dcf55ab6 --- /dev/null +++ b/client/src/app/core/services/login-data.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { LoginDataService } from './login-data.service'; + +describe('LoginDataService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [LoginDataService] + }); + }); + + it('should be created', inject([LoginDataService], (service: LoginDataService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/services/login-data.service.ts b/client/src/app/core/services/login-data.service.ts new file mode 100644 index 000000000..2f8af9f73 --- /dev/null +++ b/client/src/app/core/services/login-data.service.ts @@ -0,0 +1,76 @@ +import { Injectable } from '@angular/core'; + +import { OpenSlidesComponent } from 'app/openslides.component'; +import { ConfigService } from './config.service'; +import { BehaviorSubject, Observable } from 'rxjs'; + +/** + * This service holds the privacy policy and the legal notice, so they are available + * even if the user is not logged in. + */ +@Injectable({ + providedIn: 'root' +}) +export class LoginDataService extends OpenSlidesComponent { + /** + * Holds the privacy policy + */ + private _privacy_policy = new BehaviorSubject(''); + + /** + * Returns an observable for the privacy policy + */ + public get privacy_policy(): Observable { + return this._privacy_policy.asObservable(); + } + + /** + * Holds the legal notice + */ + private _legal_notice = new BehaviorSubject(''); + + /** + * Returns an observable for the legal notice + */ + public get legal_notice(): Observable { + return this._legal_notice.asObservable(); + } + + /** + * Constructs this service. The config service is needed to update the privacy + * policy and legal notice, when their config values change. + * @param configService + */ + public constructor(private configService: ConfigService) { + super(); + } + + /** + * Should be called, when the data store is set up. Updates the values when the + * corresponding config variables change. + */ + public setupSubscription(): void { + this.configService.get('general_event_privacy_policy').subscribe(value => { + this.setPrivacyPolicy(value); + }); + this.configService.get('general_event_legal_notice').subscribe(value => { + this.setLegalNotice(value); + }); + } + + /** + * Setter for the privacy policy + * @param privacyPolicy The new privacy policy to set + */ + public setPrivacyPolicy(privacyPolicy: string): void { + this._privacy_policy.next(privacyPolicy); + } + + /** + * Setter for the legal notice + * @param legalNotice The new legal notice to set + */ + public setLegalNotice(legalNotice: string): void { + this._legal_notice.next(legalNotice); + } +} diff --git a/client/src/app/shared/components/footer/footer.component.html b/client/src/app/shared/components/footer/footer.component.html index 505ab5842..6341b5818 100644 --- a/client/src/app/shared/components/footer/footer.component.html +++ b/client/src/app/shared/components/footer/footer.component.html @@ -1,9 +1,9 @@ - - © Copyright by OpenSlides diff --git a/client/src/app/shared/components/footer/footer.component.ts b/client/src/app/shared/components/footer/footer.component.ts index 70a3d4c73..a2e0ff87b 100644 --- a/client/src/app/shared/components/footer/footer.component.ts +++ b/client/src/app/shared/components/footer/footer.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; /** * Reusable footer Apps. @@ -18,12 +19,29 @@ import { Component, OnInit } from '@angular/core'; }) export class FooterComponent implements OnInit { /** - * Empty constructor + * Indicates to location of the legal notice */ - public constructor() {} + public legalNoticeUrl = '/legalnotice'; /** - * empty onInit + * Indicated the location of the privacy policy */ - public ngOnInit(): void {} + public privacyPolicyUrl = '/privacypolicy'; + + /** + * Empty constructor + */ + public constructor(private route: ActivatedRoute) {} + + /** + * If on login page, redirect the legal notice and privacy policy not to /URL + * but to /login/URL + */ + + public ngOnInit(): void { + if (this.route.snapshot.url[0] && this.route.snapshot.url[0].path === 'login') { + this.legalNoticeUrl = '/login/legalnotice'; + this.privacyPolicyUrl = '/login/privacypolicy'; + } + } } 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 new file mode 100644 index 000000000..0195e7320 --- /dev/null +++ b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.html @@ -0,0 +1,24 @@ + +
+ + +
+ + OpenSlides {{ versionInfo.openslides_version }} + + (License: {{ versionInfo.openslides_license }}) + +
+
Installed plugins
: +
+ + {{ plugin.verbose_name }} {{ plugin.version }} + +
+ (License: {{ plugin.license }}) +
+
+
+
+
+
diff --git a/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.scss b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.scss new file mode 100644 index 000000000..f42a956f5 --- /dev/null +++ b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.scss @@ -0,0 +1,9 @@ +.legal-notice-text { + display: block; + padding-bottom: 20px; +} + +.version-text { + display: block; + padding-top: 20px; +} diff --git a/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.spec.ts b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.spec.ts new file mode 100644 index 000000000..64313050c --- /dev/null +++ b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LegalNoticeContentComponent } from './legal-notice-content.component'; + +describe('LegalNoticeComponent', () => { + let component: LegalNoticeContentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LegalNoticeContentComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LegalNoticeContentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); 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 new file mode 100644 index 000000000..39e0cd738 --- /dev/null +++ b/client/src/app/shared/components/legal-notice-content/legal-notice-content.component.ts @@ -0,0 +1,106 @@ +import { Component, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { LoginDataService } from '../../../core/services/login-data.service'; +import { environment } from 'environments/environment'; +import { HttpClient } from '@angular/common/http'; + +/** + * Characterize a plugin. This data is retrieved from the server + */ +interface PluginDescription { + /** + * The name of the plugin + */ + verbose_name: string; + + /** + * the version + */ + version: string; + + /** + * The url to the main webpage of the plugin + */ + url: string; + + /** + * The license + */ + license: string; +} + +/** + * Represents metadata about the current installation. + */ +interface VersionResponse { + /** + * The lience string. Like 'MIT', 'GPLv2', ... + */ + openslides_license: string; + + /** + * The URl to the main webpage of OpenSlides. + */ + openslides_url: string; + + /** + * The current version. + */ + openslides_version: string; + + /** + * A list of installed plugins. + */ + plugins: PluginDescription[]; +} + +/** + * Shared component to hold the content of the Legal Notice. + * Used in login and site container. + */ +@Component({ + selector: 'os-legal-notice-content', + templateUrl: './legal-notice-content.component.html', + styleUrls: ['./legal-notice-content.component.scss'] +}) +export class LegalNoticeContentComponent implements OnInit { + /** + * The legal notive text for the ui. + */ + public legalNotice: string; + + /** + * Holds the version info retrieved from the server for the ui. + */ + public versionInfo: VersionResponse; + + /** + * Imports the LoginDataService, the translations and and HTTP Service + * @param loginDataService + * @param translate + * @param http + */ + public constructor( + private loginDataService: LoginDataService, + private translate: TranslateService, + private http: HttpClient + ) {} + + /** + * Subscribes for the legal notice text. + */ + public ngOnInit(): void { + this.loginDataService.legal_notice.subscribe(legalNotice => { + if (legalNotice) { + this.legalNotice = this.translate.instant(legalNotice); + } + }); + + // Query the version info. + this.http + .get(environment.urlPrefix + '/core/version/', {}) + .subscribe((info: VersionResponse) => { + this.versionInfo = info; + }); + } +} diff --git a/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.html b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.html new file mode 100644 index 000000000..d8ac6470b --- /dev/null +++ b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.html @@ -0,0 +1,6 @@ + +
+
+ The event manager hasn't set up a privacy policy yet. +
+
diff --git a/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.scss b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.scss new file mode 100644 index 000000000..7caed3739 --- /dev/null +++ b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.scss @@ -0,0 +1,3 @@ +mat-card { + height: 100%; +} diff --git a/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.spec.ts b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.spec.ts new file mode 100644 index 000000000..9d8a469fb --- /dev/null +++ b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PrivacyPolicyContentComponent } from './privacy-policy-content.component'; + +describe('PrivacyPolicyComponent', () => { + let component: PrivacyPolicyContentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PrivacyPolicyContentComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PrivacyPolicyContentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); 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 new file mode 100644 index 000000000..effb17627 --- /dev/null +++ b/client/src/app/shared/components/privacy-policy-content/privacy-policy-content.component.ts @@ -0,0 +1,37 @@ +import { Component, OnInit } from '@angular/core'; +import { LoginDataService } from '../../../core/services/login-data.service'; +import { TranslateService } from '@ngx-translate/core'; + +/** + * Shared component to hold the content of the Privacy Policy. + * Used in login and site container. + */ +@Component({ + selector: 'os-privacy-policy-content', + templateUrl: './privacy-policy-content.component.html', + styleUrls: ['./privacy-policy-content.component.scss'] +}) +export class PrivacyPolicyContentComponent implements OnInit { + /** + * The actual privacy policy as string + */ + public privacyPolicy: string; + + /** + * Imports the loginDataService and the translation service + * @param loginDataService Login Data + * @param translate for the translation + */ + public constructor(private loginDataService: LoginDataService, private translate: TranslateService) {} + + /** + * Subscribes for the privacy policy text + */ + public ngOnInit(): void { + this.loginDataService.privacy_policy.subscribe(privacyPolicy => { + if (privacyPolicy) { + this.privacyPolicy = this.translate.instant(privacyPolicy); + } + }); + } +} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 56fabd408..9d2a950cd 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -1,4 +1,5 @@ import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -14,7 +15,8 @@ import { MatSnackBarModule, MatTableModule, MatPaginatorModule, - MatSortModule + MatSortModule, + MatTabsModule } from '@angular/material'; import { MatDialogModule } from '@angular/material/dialog'; import { MatListModule } from '@angular/material/list'; @@ -36,8 +38,8 @@ import { PermsDirective } from './directives/perms.directive'; import { DomChangeDirective } from './directives/dom-change.directive'; import { HeadBarComponent } from './components/head-bar/head-bar.component'; import { FooterComponent } from './components/footer/footer.component'; - -import { RouterModule } from '@angular/router'; +import { LegalNoticeContentComponent } from './components/legal-notice-content/legal-notice-content.component'; +import { PrivacyPolicyContentComponent } from './components/privacy-policy-content/privacy-policy-content.component'; library.add(fas); @@ -70,10 +72,10 @@ library.add(fas); MatListModule, MatExpansionModule, MatMenuModule, - MatSnackBarModule, MatDialogModule, - TranslateModule.forChild(), + MatSnackBarModule, FontAwesomeModule, + TranslateModule.forChild(), RouterModule ], exports: [ @@ -96,13 +98,23 @@ library.add(fas); MatMenuModule, MatDialogModule, MatSnackBarModule, + MatTabsModule, FontAwesomeModule, TranslateModule, PermsDirective, DomChangeDirective, FooterComponent, - HeadBarComponent + HeadBarComponent, + LegalNoticeContentComponent, + PrivacyPolicyContentComponent ], - declarations: [PermsDirective, DomChangeDirective, HeadBarComponent, FooterComponent] + declarations: [ + PermsDirective, + DomChangeDirective, + HeadBarComponent, + FooterComponent, + LegalNoticeContentComponent, + PrivacyPolicyContentComponent + ] }) export class SharedModule {} diff --git a/client/src/app/site/legal-notice/legal-notice.component.html b/client/src/app/site/legal-notice/legal-notice.component.html index 8978d1629..6fa049b37 100644 --- a/client/src/app/site/legal-notice/legal-notice.component.html +++ b/client/src/app/site/legal-notice/legal-notice.component.html @@ -1,13 +1,3 @@ - - Legal Notice - + - -
- - -
- OpenSlides 3.0 PRE ALPHA (Lizenz: MIT) -
-
-
\ No newline at end of file + diff --git a/client/src/app/site/legal-notice/legal-notice.component.scss b/client/src/app/site/legal-notice/legal-notice.component.scss index f42a956f5..e69de29bb 100644 --- a/client/src/app/site/legal-notice/legal-notice.component.scss +++ b/client/src/app/site/legal-notice/legal-notice.component.scss @@ -1,9 +0,0 @@ -.legal-notice-text { - display: block; - padding-bottom: 20px; -} - -.version-text { - display: block; - padding-top: 20px; -} diff --git a/client/src/app/site/legal-notice/legal-notice.component.ts b/client/src/app/site/legal-notice/legal-notice.component.ts index c3b739d87..a0e874f95 100644 --- a/client/src/app/site/legal-notice/legal-notice.component.ts +++ b/client/src/app/site/legal-notice/legal-notice.component.ts @@ -1,6 +1,4 @@ import { Component, OnInit } from '@angular/core'; -import { ConfigService } from '../../core/services/config.service'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'os-legal-notice', @@ -8,15 +6,7 @@ import { TranslateService } from '@ngx-translate/core'; styleUrls: ['./legal-notice.component.scss'] }) export class LegalNoticeComponent implements OnInit { - public legalNotice: string; + public constructor() {} - public constructor(private configService: ConfigService, private translate: TranslateService) {} - - public ngOnInit(): void { - this.configService.get('general_event_legal_notice').subscribe(value => { - if (value) { - this.legalNotice = this.translate.instant(value); - } - }); - } + public ngOnInit(): void {} } diff --git a/client/src/app/site/login/assets/login-info-pages.scss b/client/src/app/site/login/assets/login-info-pages.scss new file mode 100644 index 000000000..cdc4eb7c1 --- /dev/null +++ b/client/src/app/site/login/assets/login-info-pages.scss @@ -0,0 +1,15 @@ +.title-center { + margin: 0 auto; +} + +mat-toolbar { + text-align: center; + display: grid; +} + +button { + display: block; + margin-left: auto; + margin-right: auto; + margin-top: 25px; +} diff --git a/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.html b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.html new file mode 100644 index 000000000..7592a1cf1 --- /dev/null +++ b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.html @@ -0,0 +1,9 @@ +
+ + Legal Notice + + + + + +
diff --git a/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.scss b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.spec.ts b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.spec.ts new file mode 100644 index 000000000..d9c4132c6 --- /dev/null +++ b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginLegalNoticeComponent } from './login-legal-notice.component'; + +describe('LoginLegalNoticeComponent', () => { + let component: LoginLegalNoticeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LoginLegalNoticeComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginLegalNoticeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.ts b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.ts new file mode 100644 index 000000000..44d9032b0 --- /dev/null +++ b/client/src/app/site/login/components/login-legal-notice/login-legal-notice.component.ts @@ -0,0 +1,22 @@ +import { Component, OnInit } from '@angular/core'; + +/** + * Container to display the legal notice on the login page. + * Uses the corresponding shared component + */ +@Component({ + selector: 'os-login-legal-notice', + templateUrl: './login-legal-notice.component.html', + styleUrls: ['./login-legal-notice.component.scss', '../../assets/login-info-pages.scss'] +}) +export class LoginLegalNoticeComponent implements OnInit { + /** + * Empty constructor + */ + public constructor() {} + + /** + * Empty onInit + */ + public ngOnInit(): void {} +} diff --git a/client/src/app/site/login/components/login-mask/login-mask.component.html b/client/src/app/site/login/components/login-mask/login-mask.component.html new file mode 100644 index 000000000..4f48de91b --- /dev/null +++ b/client/src/app/site/login/components/login-mask/login-mask.component.html @@ -0,0 +1,26 @@ +
+ + +
diff --git a/client/src/app/site/login/login.component.scss b/client/src/app/site/login/components/login-mask/login-mask.component.scss similarity index 58% rename from client/src/app/site/login/login.component.scss rename to client/src/app/site/login/components/login-mask/login-mask.component.scss index 7a160142a..8aa5bee02 100644 --- a/client/src/app/site/login/login.component.scss +++ b/client/src/app/site/login/components/login-mask/login-mask.component.scss @@ -2,23 +2,6 @@ mat-form-field { width: 100%; } -header { - width: 100%; - flex: 1; - mat-toolbar { - min-height: 200px !important; - } - .login-logo-bar { - img { - margin-left: auto; - margin-right: auto; - width: 90%; - height: 90%; - max-width: 400px; - } - } -} - .forgot-password-button { float: right; padding: 0; @@ -49,7 +32,3 @@ header { margin: 0 auto; max-width: 400px; } - -footer { - bottom: 0; //black magic to keep the toolbar active here -} diff --git a/client/src/app/site/login/components/login-mask/login-mask.component.spec.ts b/client/src/app/site/login/components/login-mask/login-mask.component.spec.ts new file mode 100644 index 000000000..f1aeb0851 --- /dev/null +++ b/client/src/app/site/login/components/login-mask/login-mask.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginMaskComponent } from './login-mask.component'; + +describe('LoginMaskComponent', () => { + let component: LoginMaskComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LoginMaskComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginMaskComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/login/login.component.ts b/client/src/app/site/login/components/login-mask/login-mask.component.ts similarity index 83% rename from client/src/app/site/login/login.component.ts rename to client/src/app/site/login/components/login-mask/login-mask.component.ts index dc870a76f..a7cf9588d 100644 --- a/client/src/app/site/login/login.component.ts +++ b/client/src/app/site/login/components/login-mask/login-mask.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; -import { Title } from '@angular/platform-browser'; import { BaseComponent } from 'app/base.component'; import { AuthService } from 'app/core/services/auth.service'; @@ -10,7 +9,8 @@ import { FormControl, FormGroupDirective, NgForm, FormGroup, Validators, FormBui import { TranslateService } from '@ngx-translate/core'; import { HttpErrorResponse, HttpClient } from '@angular/common/http'; import { environment } from 'environments/environment'; -import { OpenSlidesService } from '../../core/services/openslides.service'; +import { OpenSlidesService } from '../../../../core/services/openslides.service'; +import { LoginDataService } from '../../../../core/services/login-data.service'; /** * Custom error states. Might become part of the shared module later. @@ -32,16 +32,16 @@ export class ParentErrorStateMatcher implements ErrorStateMatcher { } /** - * Login component. + * Login mask component. * - * Handles user (and potentially guest) login + * Handles user and guest login */ @Component({ - selector: 'os-login', - templateUrl: './login.component.html', - styleUrls: ['./login.component.scss'] + selector: 'os-login-mask', + templateUrl: './login-mask.component.html', + styleUrls: ['./login-mask.component.scss'] }) -export class LoginComponent extends BaseComponent implements OnInit, OnDestroy { +export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestroy { /** * Show or hide password and change the indicator accordingly */ @@ -72,24 +72,19 @@ export class LoginComponent extends BaseComponent implements OnInit, OnDestroy { */ public inProcess = false; - /** - * The provacy policy send by the server. - * - * TODO: give an option to show it during login. - */ - public privacyPolicy: string; - /** * Constructor for the login component * - * @param titleService Setting the title * @param authService Authenticating the user * @param operator The representation of the current user * @param router forward to start page * @param formBuilder To build the form and validate + * @param http used to get information before the login + * @param matSnackBar Display information + * @param OpenSlides The Service for OpenSlides + * @param loginDataService provide information about the legal notice and privacy policy */ public constructor( - protected titleService: Title, protected translate: TranslateService, private authService: AuthService, private operator: OperatorService, @@ -97,9 +92,10 @@ export class LoginComponent extends BaseComponent implements OnInit, OnDestroy { private formBuilder: FormBuilder, private http: HttpClient, private matSnackBar: MatSnackBar, - private OpenSlides: OpenSlidesService + private OpenSlides: OpenSlidesService, + private loginDataService: LoginDataService ) { - super(titleService, translate); + super(); this.createForm(); } @@ -110,15 +106,15 @@ export class LoginComponent extends BaseComponent implements OnInit, OnDestroy { * Observes the operator, if a user was already logged in, recreate to user and skip the login */ public ngOnInit(): void { - super.setTitle('Login'); - + // Get the login data. Save information to the login data service this.http.get(environment.urlPrefix + '/users/login/', {}).subscribe(response => { if (response.info_text) { this.installationNotice = this.matSnackBar.open(response.info_text, this.translate.instant('OK'), { duration: 5000 }); } - this.privacyPolicy = response.privacy_policy; + this.loginDataService.setPrivacyPolicy(response.privacy_policy); + this.loginDataService.setLegalNotice(response.legal_notice); }); } diff --git a/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.html b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.html new file mode 100644 index 000000000..e327e262d --- /dev/null +++ b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.html @@ -0,0 +1,9 @@ +
+ + Privacy Policy + + + + + +
diff --git a/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.scss b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.spec.ts b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.spec.ts new file mode 100644 index 000000000..8c2a8e58e --- /dev/null +++ b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginPrivacyPolicyComponent } from './login-privacy-policy.component'; + +describe('LoginPrivacyPolicyComponent', () => { + let component: LoginPrivacyPolicyComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LoginPrivacyPolicyComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginPrivacyPolicyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.ts b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.ts new file mode 100644 index 000000000..213c70243 --- /dev/null +++ b/client/src/app/site/login/components/login-privacy-policy/login-privacy-policy.component.ts @@ -0,0 +1,22 @@ +import { Component, OnInit } from '@angular/core'; + +/** + * Container to display the privacy policy on the login page. + * Uses the corresponding shared component + */ +@Component({ + selector: 'os-login-privacy-policy', + templateUrl: './login-privacy-policy.component.html', + styleUrls: ['./login-privacy-policy.component.scss', '../../assets/login-info-pages.scss'] +}) +export class LoginPrivacyPolicyComponent implements OnInit { + /** + * Empty Constructor + */ + public constructor() {} + + /** + * Empty onInit + */ + public ngOnInit(): void {} +} diff --git a/client/src/app/site/login/components/login-wrapper/login.component.html b/client/src/app/site/login/components/login-wrapper/login.component.html new file mode 100644 index 000000000..29826247f --- /dev/null +++ b/client/src/app/site/login/components/login-wrapper/login.component.html @@ -0,0 +1,12 @@ + +
+ +
+
+ +
+
+ +
diff --git a/client/src/app/site/login/components/login-wrapper/login.component.scss b/client/src/app/site/login/components/login-wrapper/login.component.scss new file mode 100644 index 000000000..44bf6c513 --- /dev/null +++ b/client/src/app/site/login/components/login-wrapper/login.component.scss @@ -0,0 +1,16 @@ +header { + width: 100%; + flex: 1; + mat-toolbar { + min-height: 200px !important; + } + .login-logo-bar { + img { + margin-left: auto; + margin-right: auto; + width: 90%; + height: 90%; + max-width: 400px; + } + } +} diff --git a/client/src/app/site/login/login.component.spec.ts b/client/src/app/site/login/components/login-wrapper/login.component.spec.ts similarity index 100% rename from client/src/app/site/login/login.component.spec.ts rename to client/src/app/site/login/components/login-wrapper/login.component.spec.ts diff --git a/client/src/app/site/login/components/login-wrapper/login.component.ts b/client/src/app/site/login/components/login-wrapper/login.component.ts new file mode 100644 index 000000000..254a4e60f --- /dev/null +++ b/client/src/app/site/login/components/login-wrapper/login.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { TranslateService } from '@ngx-translate/core'; + +import { BaseComponent } from '../../../../base.component'; + +/** + * Login component. + * + * Serves as container for the login mask, legal notice and privacy policy + */ +@Component({ + selector: 'os-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'] +}) +export class LoginComponent extends BaseComponent implements OnInit { + /** + * Imports the title service and the translate service + * + * @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) { + super(titleService, translate); + } + + /** + * sets the title of the page + */ + public ngOnInit(): void { + super.setTitle('Login'); + } +} diff --git a/client/src/app/site/login/login.component.html b/client/src/app/site/login/login.component.html deleted file mode 100644 index 129d7bd7e..000000000 --- a/client/src/app/site/login/login.component.html +++ /dev/null @@ -1,38 +0,0 @@ - -
- -
-
-
- - - - -
- -
-
- -
diff --git a/client/src/app/site/login/login.module.ts b/client/src/app/site/login/login.module.ts index 6abe6295c..499c95e1e 100644 --- a/client/src/app/site/login/login.module.ts +++ b/client/src/app/site/login/login.module.ts @@ -1,10 +1,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { LoginComponent } from './login.component'; +import { RouterModule } from '@angular/router'; + +import { LoginComponent } from './components/login-wrapper/login.component'; import { SharedModule } from '../../shared/shared.module'; +import { LoginMaskComponent } from './components/login-mask/login-mask.component'; +import { LoginLegalNoticeComponent } from './components/login-legal-notice/login-legal-notice.component'; +import { LoginPrivacyPolicyComponent } from './components/login-privacy-policy/login-privacy-policy.component'; @NgModule({ - imports: [CommonModule, SharedModule], - declarations: [LoginComponent] + imports: [CommonModule, RouterModule, SharedModule], + declarations: [LoginComponent, LoginMaskComponent, LoginLegalNoticeComponent, LoginPrivacyPolicyComponent] }) export class LoginModule {} diff --git a/client/src/app/site/privacy-policy/privacy-policy.component.html b/client/src/app/site/privacy-policy/privacy-policy.component.html index ae8029831..98737ff13 100644 --- a/client/src/app/site/privacy-policy/privacy-policy.component.html +++ b/client/src/app/site/privacy-policy/privacy-policy.component.html @@ -1,27 +1,3 @@ - - Privacy Policy - + - -
- OpenSlides speichert nur so viele personenbezogene Daten wie unbedingt nötig sind, um Besuchern Informationen und Dienste - zuverlässig und sicher anbieten zu können. Eine Auswertung der Dienste-Protokolle erfolgt nur von uns selbst und - auch nur, um mögliche Fehler, Einbruchsversuche oder technisches Verhalten der Server auf unseren Server zu analysieren. - Eine Weitergabe von Teilen der erhobenen Daten erfolgt ausschließlich gemäß gesetzlicher Verpflichtung z.B. an Strafverfolgungs- - oder Steuerbehörden. Nachfolgend wird genau aufgeschlüsselt, bei welcher Gelegenheit welche Daten wie lange gespeichert - sind. Zudem wird beschrieben, welche Schritte erforderlich sind um Daten zu löschen. -

Cookies

- Beim Besuch der Website wird ein sogenanntes Cookie angelegt. Dieses Cookie wird ausschließlich dazu verwendet, um auf der - Website eingeloggt zu bleiben. Ein sogenanntes "Tracking Cookie" wird nicht verwendet. -

Logfiles

- Zu OpenSlides gehören verschiedene Unter-Services. Diese loggen folgende Informationen: Quell-IP-Adresse, Zeitstempel, genutztes - Betriebssystem, verwendeter Web-Browser, Referer-URL, E-Mail-Adresse und besuchte Seite. -

Database

- Als Mitglied werden folgende Daten von Ihnen gespeichert: Titel, Vorname, Nachname, EMail, Gliederungsebene, Teilnehmernummer, - Gruppenzugehörigkeit, Initiales Passwort im Klartext, Vergebenes Passwort als kryptografischer Hashwert und ein Kommentar - für interne Notizen. Diese Informationen werden mit den Aktionen innerhalb von OpenSlides in Verbindung gebracht. - Diese Informationen werden nicht an Dritte weiter gegeben und sind auch nicht für diese zugänglich. -

Deleting Files

- Die Daten dieser OpenSlides-Demo-Instanz werden nächtlich automatisch gelöscht. -
-
\ No newline at end of file + diff --git a/client/src/app/site/privacy-policy/privacy-policy.component.scss b/client/src/app/site/privacy-policy/privacy-policy.component.scss index 7caed3739..e69de29bb 100644 --- a/client/src/app/site/privacy-policy/privacy-policy.component.scss +++ b/client/src/app/site/privacy-policy/privacy-policy.component.scss @@ -1,3 +0,0 @@ -mat-card { - height: 100%; -} diff --git a/client/src/app/site/site-routing.module.ts b/client/src/app/site/site-routing.module.ts index 916bbc955..5fd843bdd 100644 --- a/client/src/app/site/site-routing.module.ts +++ b/client/src/app/site/site-routing.module.ts @@ -7,7 +7,6 @@ import { StartComponent } from './start/start.component'; import { PrivacyPolicyComponent } from './privacy-policy/privacy-policy.component'; import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; import { AuthGuard } from '../core/services/auth-guard.service'; -// import { LoginComponent } from './login/login.component'; /** * Routung to all OpenSlides apps @@ -15,15 +14,26 @@ import { AuthGuard } from '../core/services/auth-guard.service'; * TODO: Plugins will have to append to the Routes-Array */ const routes: Routes = [ - // { path: 'login', component: LoginComponent }, { path: '', component: SiteComponent, children: [ - { path: '', component: StartComponent }, - { path: 'legalnotice', component: LegalNoticeComponent }, - { path: 'privacypolicy', component: PrivacyPolicyComponent }, - { path: 'agenda', loadChildren: './agenda/agenda.module#AgendaModule' }, + { + path: '', + component: StartComponent + }, + { + path: 'legalnotice', + component: LegalNoticeComponent + }, + { + path: 'privacypolicy', + component: PrivacyPolicyComponent + }, + { + path: 'agenda', + loadChildren: './agenda/agenda.module#AgendaModule' + }, { path: 'assignments', loadChildren: './assignments/assignments.module#AssignmentsModule' @@ -32,12 +42,18 @@ const routes: Routes = [ path: 'mediafiles', loadChildren: './mediafiles/mediafiles.module#MediafilesModule' }, - { path: 'motions', loadChildren: './motions/motions.module#MotionsModule' }, + { + path: 'motions', + loadChildren: './motions/motions.module#MotionsModule' + }, { path: 'settings', loadChildren: './settings/settings.module#SettingsModule' }, - { path: 'users', loadChildren: './users/users.module#UsersModule' } + { + path: 'users', + loadChildren: './users/users.module#UsersModule' + } ], canActivateChild: [AuthGuard] } diff --git a/client/src/app/site/site.component.ts b/client/src/app/site/site.component.ts index fc09d1c58..3e7e0ed3e 100644 --- a/client/src/app/site/site.component.ts +++ b/client/src/app/site/site.component.ts @@ -44,14 +44,14 @@ export class SiteComponent extends BaseComponent implements OnInit { */ public constructor( private authService: AuthService, - operator: OperatorService, + public operator: OperatorService, public vp: ViewportService, public translate: TranslateService, public dialog: MatDialog ) { super(); - operator.getObservable().subscribe(user => { + this.operator.getObservable().subscribe(user => { if (user) { this.username = user.full_name; } else { diff --git a/client/src/app/site/start/start.component.ts b/client/src/app/site/start/start.component.ts index 94e3c53a4..552f95c65 100644 --- a/client/src/app/site/start/start.component.ts +++ b/client/src/app/site/start/start.component.ts @@ -94,11 +94,11 @@ export class StartComponent extends BaseComponent implements OnInit { console.log('the user: ', user1fromStore); console.log('remove a single user:'); - this.DS.remove(User, 100); + // this.DS.remove(User, 100); console.log('remove more users'); - this.DS.remove(User, 200, 201, 202); + // this.DS.remove(User, 200, 201, 202); console.log('remove an array of users'); - this.DS.remove(User, ...[321, 363, 399]); + // this.DS.remove(User, ...[321, 363, 399]); console.log('test filter: '); console.log(this.DS.filter(User, user => user.id === 1)); diff --git a/openslides/users/views.py b/openslides/users/views.py index 2622db1b9..337e4801c 100644 --- a/openslides/users/views.py +++ b/openslides/users/views.py @@ -453,8 +453,10 @@ class UserLoginView(APIView): password='admin') else: context['info_text'] = '' - # Add the privacy policy, so the client can display it even, it is not logged in. + # 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'] + context['legal_notice'] = config['general_event_legal_notice'] else: # self.request.method == 'POST' context['user_id'] = self.user.pk