From 3a29297e886c8f4cc247e065df9a60d6177514a2 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Thu, 02 Mar 2017 15:52:51 +0000
Subject: [PATCH] #3527 feature - Added common components like i18n and local storage
---
securis/src/main/webapp/src/environments/environment.prod.ts | 3
securis/src/main/webapp/src/app/app.component.ts | 28 ++
securis/src/main/webapp/src/environments/environment.ts | 3
securis/src/main/webapp/src/lang/messages_fr.json | 4
securis/src/main/webapp/src/environments/environment.dev.ts | 3
securis/src/main/webapp/src/main.ts | 6
securis/src/main/webapp/src/app/app.routes.ts | 4
securis/src/main/webapp/package.json | 20 +-
securis/src/main/webapp/src/app/common/i18n.ts | 133 ++++++++++++++++
securis/src/main/webapp/src/app/login.form.component.ts | 38 ++++
securis/src/main/webapp/src/app/login.form.html | 25 +++
securis/src/main/webapp/.vscode/settings.json | 4
securis/src/main/webapp/src/app/common/default.requests.options.ts | 37 ++++
securis/src/main/webapp/src/app/app.module.ts | 18 ++
securis/src/main/webapp/src/app/user.service.ts | 76 +++++++++
securis/src/main/webapp/src/lang/messages_es.json | 4
securis/src/main/webapp/src/app/pack.list.component.ts | 1
securis/src/main/webapp/systemjs.config.js | 10 +
securis/src/main/webapp/src/lang/messages_en.json | 4
19 files changed, 403 insertions(+), 18 deletions(-)
diff --git a/securis/src/main/webapp/.vscode/settings.json b/securis/src/main/webapp/.vscode/settings.json
new file mode 100644
index 0000000..0c807a4
--- /dev/null
+++ b/securis/src/main/webapp/.vscode/settings.json
@@ -0,0 +1,4 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "typescript.tsdk": "./node_modules/typescript/lib"
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/package.json b/securis/src/main/webapp/package.json
index 3d12b5f..509fc75 100644
--- a/securis/src/main/webapp/package.json
+++ b/securis/src/main/webapp/package.json
@@ -1,6 +1,6 @@
{
- "name": "angular-quickstart",
- "version": "1.0.0",
+ "name": "securis",
+ "version": "2.0.0",
"description": "QuickStart package.json from the documentation, supplemented with testing support",
"scripts": {
"build": "tsc -p src/",
@@ -36,30 +36,32 @@
"@covalent/core": "^1.0.0-beta.2",
"@covalent/dynamic-forms": "^1.0.0-beta.2",
"@covalent/http": "^1.0.0-beta.2",
+ "angular-2-local-storage": "^1.0.1",
"angular-in-memory-web-api": "~0.2.4",
"core-js": "^2.4.1",
"hammerjs": "^2.0.8",
+ "ng2-toastr": "^1.5.1",
"rxjs": "^5.0.1",
"systemjs": "0.19.40",
"zone.js": "^0.7.4"
},
"devDependencies": {
- "concurrently": "^3.2.0",
- "lite-server": "^2.2.2",
- "typescript": "~2.0.10",
+ "@types/jasmine": "2.5.36",
+ "@types/node": "^6.0.46",
"canonical-path": "0.0.2",
- "tslint": "^3.15.1",
- "lodash": "^4.16.4",
+ "concurrently": "^3.2.0",
"jasmine-core": "~2.4.1",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-jasmine-html-reporter": "^0.2.2",
+ "lite-server": "^2.2.2",
+ "lodash": "^4.16.4",
"protractor": "~4.0.14",
"rimraf": "^2.5.4",
- "@types/node": "^6.0.46",
- "@types/jasmine": "2.5.36"
+ "tslint": "^3.15.1",
+ "typescript": "^2.2.1"
},
"repository": {}
}
diff --git a/securis/src/main/webapp/src/app/app.component.ts b/securis/src/main/webapp/src/app/app.component.ts
index 74afbfe..786b8d0 100644
--- a/securis/src/main/webapp/src/app/app.component.ts
+++ b/securis/src/main/webapp/src/app/app.component.ts
@@ -1,6 +1,10 @@
-import { Component } from '@angular/core';
+import { Component, AfterViewInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { MdIconRegistry } from '@angular/material';
+import { UserService } from './user.service';
+import { LocalStorageService } from 'angular-2-local-storage';
+import { TdMediaService } from '@covalent/core';
+import { Router } from '@angular/router';
// https://github.com/Teradata/covalent-quickstart/tree/develop/src/app
// https://teradata.github.io/covalent-quickstart/#/
@@ -11,11 +15,18 @@
})
-export class AppComponent {
+export class AppComponent implements AfterViewInit {
- constructor(private _iconRegistry: MdIconRegistry,
- private _domSanitizer: DomSanitizer) {
-
+ constructor(private userService: UserService,
+ private router: Router,
+ private media: TdMediaService,
+ private _iconRegistry: MdIconRegistry,
+ private _domSanitizer: DomSanitizer,
+ private store: LocalStorageService) {
+ this.registerIcons();
+ }
+
+ private registerIcons() : void {
this._iconRegistry.addSvgIconInNamespace('assets', 'covalent',
this._domSanitizer.bypassSecurityTrustResourceUrl('https://raw.githubusercontent.com/Teradata/covalent-quickstart/develop/src/assets/icons/covalent.svg'));
this._iconRegistry.addSvgIconInNamespace('assets', 'teradata',
@@ -36,4 +47,11 @@
this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/querygrid.svg'));
}
+ ngAfterViewInit(): void {
+ this.media.broadcast();
+ this.userService.isLoggedIn().subscribe(authOk => {
+ this.router.navigateByUrl(authOk ? 'packs' : 'login');
+ },
+ err => /* Show message */ this.router.navigateByUrl('login'));
+ }
}
diff --git a/securis/src/main/webapp/src/app/app.module.ts b/securis/src/main/webapp/src/app/app.module.ts
index 38fc432..9dc09d5 100644
--- a/securis/src/main/webapp/src/app/app.module.ts
+++ b/securis/src/main/webapp/src/app/app.module.ts
@@ -7,30 +7,46 @@
import {CovalentHttpModule} from '@covalent/http';
import {CovalentDynamicFormsModule} from '@covalent/dynamic-forms';
+import { LocalStorageModule } from 'angular-2-local-storage';
+import { ToastModule } from 'ng2-toastr/ng2-toastr';
+
import { AppComponent } from './app.component';
+import { UserService } from './user.service';
import { PackListComponent } from './pack.list.component';
import { HeroDetailComponent } from './detail.component';
+import { LoginFormComponent } from './login.form.component';
import { appRoutes, appRoutingProviders } from './app.routes';
+import { requestOptionsProvider } from './common/default.requests.options';
+import { LocaleServiceModule } from './common/i18n';
@NgModule({
imports: [
+ LocalStorageModule.withConfig({
+ prefix: 'securis',
+ storageType: 'localStorage'
+ }),
BrowserModule,
FormsModule,
- MaterialModule.forRoot(),
+ MaterialModule,
CovalentCoreModule.forRoot(),
CovalentHttpModule.forRoot(),
CovalentDynamicFormsModule.forRoot(),
+ ToastModule.forRoot(),
+ LocaleServiceModule,
appRoutes,
],
declarations: [
HeroDetailComponent,
PackListComponent,
+ LoginFormComponent,
AppComponent
],
bootstrap: [ AppComponent ],
providers: [
+ UserService,
appRoutingProviders,
+ requestOptionsProvider,
]
})
diff --git a/securis/src/main/webapp/src/app/app.routes.ts b/securis/src/main/webapp/src/app/app.routes.ts
index 4078919..a8c5fac 100644
--- a/securis/src/main/webapp/src/app/app.routes.ts
+++ b/securis/src/main/webapp/src/app/app.routes.ts
@@ -2,10 +2,12 @@
import { AppComponent } from './app.component';
import { PackListComponent } from './pack.list.component';
+import { LoginFormComponent } from './login.form.component';
const routes: Routes = [
- {path: '', redirectTo: '/packs', pathMatch: 'full'},
+ // {path: '', redirectTo: '/packs', pathMatch: 'full'},
{path: 'packs', component: PackListComponent },
+ {path: 'login', component: LoginFormComponent }
];
/* {path: 'product', component: DashboardProductComponent, children: [
{path: '', component: ProductOverviewComponent},
diff --git a/securis/src/main/webapp/src/app/common/default.requests.options.ts b/securis/src/main/webapp/src/app/common/default.requests.options.ts
new file mode 100644
index 0000000..62dbe13
--- /dev/null
+++ b/securis/src/main/webapp/src/app/common/default.requests.options.ts
@@ -0,0 +1,37 @@
+import { LocalStorageService } from 'angular-2-local-storage';
+import { Injectable } from '@angular/core';
+import { BaseRequestOptions, RequestOptions, Request, XHRBackend, XHRConnection} from '@angular/http';
+
+
+// TODO: Chnage this to use Covalent Http helper service
+// https://www.npmjs.com/package/@covalent/http
+
+@Injectable()
+export class DefaultRequestOptions extends BaseRequestOptions {
+
+ constructor(private store: LocalStorageService) {
+ super();
+
+ // Set the default 'Content-Type' header
+ this.headers.set('Content-Type', 'application/json');
+ let token = this.store.get<string>('token');
+ if (token) {
+ this.headers.set('X-SECURIS-TOKEN', token);
+ }
+
+ }
+}
+
+@Injectable()
+export class ApiXHRBackend extends XHRBackend {
+ createConnection(request: Request): XHRConnection {
+ if (request.url.startsWith('/')){
+ // request.url = '/securis' + request.url; // prefix base url
+ }
+ return super.createConnection(request);
+ }
+}
+
+export const requestOptionsProvider = { provide: RequestOptions, useClass: DefaultRequestOptions };
+
+export const requestBackendProvider = { provide: XHRBackend, useClass: ApiXHRBackend };
diff --git a/securis/src/main/webapp/src/app/common/i18n.ts b/securis/src/main/webapp/src/app/common/i18n.ts
new file mode 100644
index 0000000..f7ca62c
--- /dev/null
+++ b/securis/src/main/webapp/src/app/common/i18n.ts
@@ -0,0 +1,133 @@
+import { Http } from '@angular/http';
+import { ModuleWithProviders, NgModule, Inject, Injectable, Directive, ElementRef, HostListener, Input, Renderer } from '@angular/core';
+declare var navigator:any;
+
+// Use as reference: https://github.com/ngx-translate/core/tree/master/src
+@Injectable()
+export class LocaleService {
+ get URL_TPL() {return 'src/lang/messages-{0}.json'};
+
+ private _currentLang : string = null;
+ private _messages : any = null;
+ private _elements = new Array<ElementRef>();
+ private constructor(private http: Http, @Inject('INITIAL_LANG') initLang: string) {
+ this._currentLang = initLang || this.getBrowserLang();
+ }
+
+ get lang() : string {
+ return this._currentLang;
+ }
+
+ set lang(newLang: string) {
+ this._currentLang = newLang;
+ this.http.get(this._format(this.URL_TPL, newLang)).subscribe((data) => {
+ this._messages = data;
+ this.reloadTexts();
+ });
+ }
+
+ isLoaded() : boolean {
+ return !!this._messages
+ }
+
+ getBrowserLang() : string {
+ var l;
+ if (!navigator) return 'en';
+
+ if (navigator.languages && navigator.languages.length) {
+ l = navigator.languages[0];
+ } else {
+ l = navigator.language || 'en';
+ }
+ l = l.replace(/-.*/gi, '');
+ if (l !== 'en' && l !== 'es')
+ return 'en';
+ return l;
+ }
+
+ /**
+ * It works similar to MessageFormat in Java
+ */
+ _format(str: string, ...params: Array<string>) {
+
+ return str.replace(/\{(\d+)\}/g, function(match, index) {
+ return params[index];
+ });
+ };
+
+ reloadTexts() : void {
+ this._elements.forEach((ele: ElementRef) => {
+
+ var txt = ele.nativeElement.getAttribute('i18n');
+ if (!!txt) {
+ ele.nativeElement.textContent = this.get(txt);
+ }
+ })
+ }
+
+ add(ele: ElementRef) {
+ this._elements.push(ele);
+ }
+
+ /**
+ * It accepts direct messages and templates:
+ * {
+ * "hello": "hola",
+ * "Hello {0}!!: "Hola {0}!!"
+ * }
+ * $L.get('hello'); // This returns "hola"
+ * $L.get('Hello {0}!!', 'John'); // This returns: "Hola John!!" if language is spanish
+ */
+ get(msg: string) : string {
+ msg = msg.trim();
+ var trans_msg = msg;
+
+ if (this.isLoaded()) {
+ if (this._messages[msg]) {
+ trans_msg = this._messages[msg];
+ } else if (this._messages[msg.toLowerCase()]) {
+ trans_msg = this._messages[msg.toLowerCase()];
+ } else {
+ this._currentLang !== 'es' && console.error("Missing i18 key: " + msg);
+ trans_msg = msg;
+ }
+ }
+ // Enviar evento cuando el idioma cambia al $rootScope
+
+ if (arguments.length === 1) return trans_msg;
+ var params = Array.prototype.slice.call(arguments, 1);
+ return this._format.apply(trans_msg, params);
+ }
+
+}
+
+
+@Directive({ selector: '[i18n]' })
+export class I18nDirective {
+ constructor(private el: ElementRef, private renderer: Renderer, private $L: LocaleService) {
+ }
+
+ ngAfterViewChecked() {
+ var txt = this.el.nativeElement.getAttribute('i18n') || this.el.nativeElement.textContent;
+ this.renderer.setElementProperty(this.el.nativeElement, 'i18n', txt.trim());
+ this.$L.add(this.el);
+
+ this.renderer.setElementProperty(this.el.nativeElement, 'innerHTML', this.$L.get(txt));
+ }
+}
+
+@NgModule({
+ providers: [ <any>LocaleService ]
+})
+export class LocaleServiceModule {
+ static withConfig(initLang?: string): ModuleWithProviders {
+ return {
+ ngModule: LocaleServiceModule,
+ providers: [
+ { provide: 'INITIAL_LANG', useValue: initLang }
+ ]
+ }
+ }
+}
+
+
diff --git a/securis/src/main/webapp/src/app/login.form.component.ts b/securis/src/main/webapp/src/app/login.form.component.ts
new file mode 100644
index 0000000..7de13b0
--- /dev/null
+++ b/securis/src/main/webapp/src/app/login.form.component.ts
@@ -0,0 +1,38 @@
+import { Http } from '@angular/http';
+import { Router } from '@angular/router';
+import { AfterViewInit, Component, Input } from '@angular/core';
+import { UserService } from './user.service';
+import { ToastsManager } from 'ng2-toastr/ng2-toastr';
+
+class LoginData {
+ username: string;
+ password: string;
+}
+
+@Component({
+ selector: 'login-form',
+ templateUrl: "src/app/login.form.html"
+})
+export class LoginFormComponent implements AfterViewInit {
+ data = new LoginData();
+
+ public constructor(private toaster: ToastsManager,
+ private router: Router,
+ private userService: UserService) {
+ }
+
+ public login() {
+ this.userService.login(this.data.username, this.data.password).subscribe(
+ token => this.toaster.info('Hola'),
+ err => this.toaster.error('Hubo un error: ' + err));
+ }
+
+ public clear() {
+ this.data.username = '';
+ this.data.password = '';
+ }
+
+ ngAfterViewInit() {
+
+ }
+}
diff --git a/securis/src/main/webapp/src/app/login.form.html b/securis/src/main/webapp/src/app/login.form.html
new file mode 100644
index 0000000..3d9d09e
--- /dev/null
+++ b/securis/src/main/webapp/src/app/login.form.html
@@ -0,0 +1,25 @@
+<md-card>
+ <md-card-title layout="row" layout-align="start end"><md-icon class="md-icon-logo" svgSrc="assets/icons/logo.svg"></md-icon> <span class="md-app-title" i18n>
+ SeCuris Login</span></md-card-title>
+ <md-card-subtitle>Sign in via your current SeCuris account</md-card-subtitle>
+ <md-divider></md-divider>
+ <md-card-content>
+ <form #loginForm="ngForm" class="inset">
+ <div layout="row">
+ <md-input-container>
+ <input mdInput flex placeholder="Username" type="text" [(ngModel)]="data.username" name="username" required />
+ </md-input-container>
+ </div>
+ <div layout="row">
+ <md-input-container>
+ <input mdInput (keyup.enter)="loginForm.form.valid && login()" flex placeholder="Password" type="password" [(ngModel)]="data.password" name="password" required/>
+ </md-input-container>
+ </div>
+ </form>
+ <div id="invalid-auth-msg" *ngIf="invalidError" class="tc-red-600 text-center" flex i18n>The username or password is incorrect. Please try again.</div>
+ </md-card-content>
+ <md-divider></md-divider>
+ <md-card-actions>
+ <button flex [disabled]="!loginForm.form.valid" md-raised-button color="primary" (click)="login()">Sign In</button>
+ </md-card-actions>
+</md-card>
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/pack.list.component.ts b/securis/src/main/webapp/src/app/pack.list.component.ts
index 4d51722..2355901 100644
--- a/securis/src/main/webapp/src/app/pack.list.component.ts
+++ b/securis/src/main/webapp/src/app/pack.list.component.ts
@@ -1,3 +1,4 @@
+import { Hero } from './hero';
import { Component, AfterViewInit } from '@angular/core';
import { TdMediaService } from '@covalent/core';
diff --git a/securis/src/main/webapp/src/app/user.service.ts b/securis/src/main/webapp/src/app/user.service.ts
new file mode 100644
index 0000000..38d842d
--- /dev/null
+++ b/securis/src/main/webapp/src/app/user.service.ts
@@ -0,0 +1,76 @@
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+import { Location } from '@angular/common';
+import { Http, RequestOptions, Response, Headers } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+
+import { LocalStorageService } from 'angular-2-local-storage';
+
+const SECURIS_TOKEN = "X-SECURIS-TOKEN";
+
+@Injectable()
+export class UserService {
+
+ constructor(private router: Router,
+ private store: LocalStorageService,
+ private http: Http) {
+
+ }
+
+ public login(username: string, password: string) : Observable<string> {
+ let params = new URLSearchParams();
+ params.append('username', username);
+ params.append('password', password);
+ let options = new RequestOptions({ headers: new Headers({ "Content-Type": "application/x-www-form-urlencoded" })});
+ return this.http.post('user/login', params, options)
+ .map((res: Response) => {
+ let data = res.json();
+ this.store.set('username', username);
+ this.store.set('token', data.token);
+ return <string>data.token;
+ })
+ .catch(this.handleError);
+ }
+
+ isLoggedIn() : Observable<Boolean> {
+ if (!this.existsToken()) {
+ return Observable.of(false);
+ }
+ var token = this.store.get(SECURIS_TOKEN);
+ return this.http.get('check', new RequestOptions({ headers: new Headers({ 'X-SECURIS-TOKEN': token }) }))
+ .map((res: Response) => {
+ let body = res.json();
+ if (body.valid) {
+ this.store.set('user', body.user);
+ }
+ return body.valid;
+ })
+ .catch(this.handleError)
+ .catch(() => Observable.of(false));
+ }
+
+ existsToken() : Boolean {
+ return this.store.get(SECURIS_TOKEN) !== null;
+ }
+
+ logout() : void {
+ this.store.remove('user', 'token');
+ this.router.navigate(['Login']);
+ }
+
+ private handleError (error: Response | any) {
+ // In a real world app, we might use a remote logging infrastructure
+ let errMsg: string;
+ if (error instanceof Response) {
+ const body = error.json() || '';
+ const err = body.error || JSON.stringify(body);
+ errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
+ } else {
+ errMsg = error.message ? error.message : error.toString();
+ }
+ console.error(errMsg);
+ return Observable.throw(errMsg);
+ }
+
+}
+
diff --git a/securis/src/main/webapp/src/environments/environment.dev.ts b/securis/src/main/webapp/src/environments/environment.dev.ts
new file mode 100644
index 0000000..ffe8aed
--- /dev/null
+++ b/securis/src/main/webapp/src/environments/environment.dev.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: false
+};
diff --git a/securis/src/main/webapp/src/environments/environment.prod.ts b/securis/src/main/webapp/src/environments/environment.prod.ts
new file mode 100644
index 0000000..3612073
--- /dev/null
+++ b/securis/src/main/webapp/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/securis/src/main/webapp/src/environments/environment.ts b/securis/src/main/webapp/src/environments/environment.ts
index e69de29..ffe8aed 100644
--- a/securis/src/main/webapp/src/environments/environment.ts
+++ b/securis/src/main/webapp/src/environments/environment.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: false
+};
diff --git a/securis/src/main/webapp/src/lang/messages_en.json b/securis/src/main/webapp/src/lang/messages_en.json
new file mode 100644
index 0000000..413715e
--- /dev/null
+++ b/securis/src/main/webapp/src/lang/messages_en.json
@@ -0,0 +1,4 @@
+{
+"": "",
+"": ""
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/lang/messages_es.json b/securis/src/main/webapp/src/lang/messages_es.json
new file mode 100644
index 0000000..413715e
--- /dev/null
+++ b/securis/src/main/webapp/src/lang/messages_es.json
@@ -0,0 +1,4 @@
+{
+"": "",
+"": ""
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/lang/messages_fr.json b/securis/src/main/webapp/src/lang/messages_fr.json
new file mode 100644
index 0000000..413715e
--- /dev/null
+++ b/securis/src/main/webapp/src/lang/messages_fr.json
@@ -0,0 +1,4 @@
+{
+"": "",
+"": ""
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/main.ts b/securis/src/main/webapp/src/main.ts
index 923ac5b..a75fbfe 100644
--- a/securis/src/main/webapp/src/main.ts
+++ b/securis/src/main/webapp/src/main.ts
@@ -4,6 +4,10 @@
import { AppModule } from './app/app.module';
import { enableProdMode } from '@angular/core';
-enableProdMode();
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/securis/src/main/webapp/systemjs.config.js b/securis/src/main/webapp/systemjs.config.js
index 3612986..4567190 100644
--- a/securis/src/main/webapp/systemjs.config.js
+++ b/securis/src/main/webapp/systemjs.config.js
@@ -9,11 +9,14 @@
var COVALENT_LIBS = ['core', 'http', 'dynamic-forms'];
var mapping = {
// our app is within the app folder
- main: 'src/main.js',
+ main: 'src/main.js',
'app': 'src/app',
+ 'environments': 'src/environments',
// other libraries
'rxjs': 'npm:rxjs',
+ 'ng2-toastr': 'npm:ng2-toastr',
+ 'angular-2-local-storage': 'npm:angular-2-local-storage/dist',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
}
@@ -32,10 +35,15 @@
},
map: mapping,
packages: {
+ 'environments': {
+ defaultExtension: 'js'
+ },
'app': {
defaultExtension: 'js'
},
'rxjs': { main: 'index' },
+ 'angular-2-local-storage': { main: 'index', defaultExtension: 'js' },
+ 'ng2-toastr': { defaultExtension: 'js' }
}
});
--
Gitblit v1.3.2