Merge pull request #3864 from tsiegleauq/restructure_pp_ln
Routing for Privacy Policy and Legal Notice + Restructure Login Components
This commit is contained in:
commit
44b287248c
@ -1,4 +1,8 @@
|
||||
{
|
||||
"/apps/core/version": {
|
||||
"target": "http://localhost:8000",
|
||||
"secure": false
|
||||
},
|
||||
"/apps/users/login": {
|
||||
"target": "http://localhost:8000",
|
||||
"secure": false
|
||||
|
@ -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: '' }
|
||||
|
@ -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!
|
||||
}
|
||||
|
@ -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]) {
|
||||
|
15
client/src/app/core/services/login-data.service.spec.ts
Normal file
15
client/src/app/core/services/login-data.service.spec.ts
Normal file
@ -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();
|
||||
}));
|
||||
});
|
76
client/src/app/core/services/login-data.service.ts
Normal file
76
client/src/app/core/services/login-data.service.ts
Normal file
@ -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<string>('');
|
||||
|
||||
/**
|
||||
* Returns an observable for the privacy policy
|
||||
*/
|
||||
public get privacy_policy(): Observable<string> {
|
||||
return this._privacy_policy.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the legal notice
|
||||
*/
|
||||
private _legal_notice = new BehaviorSubject<string>('');
|
||||
|
||||
/**
|
||||
* Returns an observable for the legal notice
|
||||
*/
|
||||
public get legal_notice(): Observable<string> {
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
<mat-toolbar color='primary' class="footer">
|
||||
<mat-toolbar-row>
|
||||
<button mat-button class="footer-link" [routerLink]="['/legalnotice']">
|
||||
<button mat-button class="footer-link" [routerLink]=legalNoticeUrl>
|
||||
<span translate>Legal Notice</span>
|
||||
</button>
|
||||
<button mat-button class="footer-link" [routerLink]="['/privacypolicy']">
|
||||
<button mat-button class="footer-link" [routerLink]=privacyPolicyUrl>
|
||||
<span translate>Privacy Policy</span>
|
||||
</button>
|
||||
<span class="footer-right">© <span translate>Copyright by</span> <a href='https://openslides.org/'>OpenSlides</a>
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
<mat-card class="os-card on-transition-fade">
|
||||
<div>
|
||||
<div class='legal-notice-text' [innerHtml]='legalNotice'></div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="versionInfo" class='version-text'>
|
||||
<a [attr.href]="versionInfo.openslides_url" target="_blank">
|
||||
OpenSlides {{ versionInfo.openslides_version }}
|
||||
</a>
|
||||
(<span translate>License</span>: {{ versionInfo.openslides_license }})
|
||||
|
||||
<div *ngIf="versionInfo.plugins.length">
|
||||
<div translate>Installed plugins</div>:
|
||||
<div *ngFor="let plugin of versionInfo.plugins">
|
||||
<a [attr.href]="plugin.url" target="_blank">
|
||||
{{ plugin.verbose_name }} {{ plugin.version }}
|
||||
</a>
|
||||
<div *ngIf="plugin.license">
|
||||
(<span translate>License</span>: {{ plugin.license }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
@ -0,0 +1,9 @@
|
||||
.legal-notice-text {
|
||||
display: block;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.version-text {
|
||||
display: block;
|
||||
padding-top: 20px;
|
||||
}
|
@ -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<LegalNoticeContentComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LegalNoticeContentComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LegalNoticeContentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -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<VersionResponse>(environment.urlPrefix + '/core/version/', {})
|
||||
.subscribe((info: VersionResponse) => {
|
||||
this.versionInfo = info;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<mat-card class='os-card on-transition-fade'>
|
||||
<div *ngIf='privacyPolicy' [innerHtml]='privacyPolicy'></div>
|
||||
<div *ngIf='!privacyPolicy' translate>
|
||||
The event manager hasn't set up a privacy policy yet.
|
||||
</div>
|
||||
</mat-card>
|
@ -0,0 +1,3 @@
|
||||
mat-card {
|
||||
height: 100%;
|
||||
}
|
@ -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<PrivacyPolicyContentComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PrivacyPolicyContentComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PrivacyPolicyContentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
|
@ -1,13 +1,3 @@
|
||||
<mat-toolbar color='primary'>
|
||||
<span class='app-name on-transition-fade' translate>Legal Notice</span>
|
||||
</mat-toolbar>
|
||||
<os-head-bar appName="Legal Notice"></os-head-bar>
|
||||
|
||||
<mat-card class="os-card on-transition-fade">
|
||||
<div>
|
||||
<div class='legal-notice-text' [innerHtml]='legalNotice'></div>
|
||||
<mat-divider></mat-divider>
|
||||
<div class='version-text'>
|
||||
OpenSlides 3.0 PRE ALPHA (Lizenz: MIT)
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
<os-legal-notice-content></os-legal-notice-content>
|
||||
|
@ -1,9 +0,0 @@
|
||||
.legal-notice-text {
|
||||
display: block;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.version-text {
|
||||
display: block;
|
||||
padding-top: 20px;
|
||||
}
|
@ -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 {}
|
||||
}
|
||||
|
15
client/src/app/site/login/assets/login-info-pages.scss
Normal file
15
client/src/app/site/login/assets/login-info-pages.scss
Normal file
@ -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;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<main>
|
||||
<mat-toolbar color="primary" translate>
|
||||
Legal Notice
|
||||
</mat-toolbar>
|
||||
|
||||
<os-legal-notice-content></os-legal-notice-content>
|
||||
|
||||
<button mat-raised-button color="accent" routerLink="/login" routerLinkActive="router-link-active" translate>Login</button>
|
||||
</main>
|
@ -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<LoginLegalNoticeComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LoginLegalNoticeComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginLegalNoticeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -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 {}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<div class="form-wrapper">
|
||||
<mat-spinner *ngIf="inProcess"></mat-spinner>
|
||||
<form [formGroup]="loginForm" class="login-form" (ngSubmit)="formLogin()">
|
||||
<mat-form-field>
|
||||
<input matInput required placeholder="User name" formControlName="username" [errorStateMatcher]="parentErrorStateMatcher">
|
||||
</mat-form-field>
|
||||
<br>
|
||||
<mat-form-field>
|
||||
<input matInput required placeholder="Password" formControlName="password" [type]="!hide ? 'password' : 'text'"
|
||||
[errorStateMatcher]="parentErrorStateMatcher">
|
||||
<fa-icon matSuffix [icon]="!hide ? 'eye-slash' : 'eye'" (click)="hide = !hide"></fa-icon>
|
||||
<mat-error>{{loginErrorMsg}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- forgot password button -->
|
||||
<br>
|
||||
<button type="button" class='forgot-password-button' (click)="resetPassword()" mat-button>Forgot Password?</button>
|
||||
|
||||
<!-- login button -->
|
||||
<br>
|
||||
<!-- TODO: Next to each other...-->
|
||||
<button mat-raised-button color="primary" class='login-button' type="submit" translate>Login</button>
|
||||
<button mat-raised-button *ngIf="areGuestsEnabled()" color="primary" class='login-button' type="button"
|
||||
(click)="guestLogin()" translate>Login as Guest</button>
|
||||
</form>
|
||||
</div>
|
@ -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
|
||||
}
|
@ -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<LoginMaskComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LoginMaskComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginMaskComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -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<any>(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);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
<main>
|
||||
<mat-toolbar color="primary" translate>
|
||||
Privacy Policy
|
||||
</mat-toolbar>
|
||||
|
||||
<os-privacy-policy-content></os-privacy-policy-content>
|
||||
|
||||
<button mat-raised-button color="accent" routerLink="/login" routerLinkActive="router-link-active" translate>Login</button>
|
||||
</main>
|
@ -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<LoginPrivacyPolicyComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LoginPrivacyPolicyComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginPrivacyPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -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 {}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<!-- The actual form -->
|
||||
<header>
|
||||
<mat-toolbar class="login-logo-bar" color="primary">
|
||||
<img src='/assets/img/openslides-logo-h-dark-transparent.svg' alt='OpenSlides-logo'>
|
||||
</mat-toolbar>
|
||||
</header>
|
||||
<main>
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
<footer class="page-footer">
|
||||
<os-footer></os-footer>
|
||||
</footer>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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');
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<!-- The actual form -->
|
||||
<header>
|
||||
<mat-toolbar class="login-logo-bar" color="primary">
|
||||
<img src='/assets/img/openslides-logo-h-dark-transparent.svg' alt='OpenSlides-logo'>
|
||||
</mat-toolbar>
|
||||
</header>
|
||||
<main>
|
||||
<div class="form-wrapper">
|
||||
|
||||
<mat-spinner *ngIf="inProcess"></mat-spinner>
|
||||
<form [formGroup]="loginForm" class="login-form" (ngSubmit)="formLogin()">
|
||||
<mat-form-field>
|
||||
<input matInput required placeholder="User name" formControlName="username" [errorStateMatcher]="parentErrorStateMatcher">
|
||||
</mat-form-field>
|
||||
<br>
|
||||
<mat-form-field>
|
||||
<input matInput required placeholder="Password" formControlName="password" [type]="!hide ? 'password' : 'text'" [errorStateMatcher]="parentErrorStateMatcher">
|
||||
<fa-icon matSuffix [icon]="!hide ? 'eye-slash' : 'eye'" (click)="hide = !hide"></fa-icon>
|
||||
<mat-error>{{loginErrorMsg}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- forgot password button -->
|
||||
<br>
|
||||
<button type="button" class='forgot-password-button' (click)="resetPassword()" mat-button>Forgot Password?</button>
|
||||
|
||||
<!-- login button -->
|
||||
<br>
|
||||
<!-- TODO: Next to each other...-->
|
||||
<button mat-raised-button color="primary" class='login-button' type="submit" translate>Login</button>
|
||||
<button mat-raised-button *ngIf="areGuestsEnabled()" color="primary" class='login-button' type="button" (click)="guestLogin()" translate>Login as Guest</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
<footer class="page-footer">
|
||||
<os-footer></os-footer>
|
||||
</footer>
|
@ -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 {}
|
||||
|
@ -1,27 +1,3 @@
|
||||
<mat-toolbar color='primary'>
|
||||
<span class='app-name on-transition-fade' translate>Privacy Policy</span>
|
||||
</mat-toolbar>
|
||||
<os-head-bar appName="Privacy Policy"></os-head-bar>
|
||||
|
||||
<mat-card class="os-card on-transition-fade">
|
||||
<div>
|
||||
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.
|
||||
<h3 translate>Cookies</h3>
|
||||
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.
|
||||
<h3 translate>Logfiles</h3>
|
||||
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.
|
||||
<h3 translate>Database</h3>
|
||||
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.
|
||||
<h3 translate>Deleting Files</h3>
|
||||
Die Daten dieser OpenSlides-Demo-Instanz werden nächtlich automatisch gelöscht.
|
||||
</div>
|
||||
</mat-card>
|
||||
<os-privacy-policy-content></os-privacy-policy-content>
|
||||
|
@ -1,3 +0,0 @@
|
||||
mat-card {
|
||||
height: 100%;
|
||||
}
|
@ -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]
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 => user.id === 1));
|
||||
|
@ -453,8 +453,10 @@ class UserLoginView(APIView):
|
||||
password='<strong>admin</strong>')
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user