--- /dev/null
+<h3>Account</h3>
+
+<div *ngIf="information" class="alert alert-success">{{ information }}</div>
+<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
+
+<form role="form" (ngSubmit)="changePassword(newPassword.value, newConfirmedPassword.value)" [ngFormModel]="changePasswordForm">
+ <div class="form-group">
+ <label for="new-password">New password</label>
+ <input
+ type="password" class="form-control" name="new-password" id="new-password"
+ ngControl="newPassword" #newPassword="ngForm"
+ >
+ <div [hidden]="newPassword.valid || newPassword.pristine" class="alert alert-warning">
+ The password should have more than 5 characters
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="name">Confirm new password</label>
+ <input
+ type="password" class="form-control" name="new-confirmed-password" id="new-confirmed-password"
+ ngControl="newConfirmedPassword" #newConfirmedPassword="ngForm"
+ >
+ </div>
+
+ <input type="submit" value="Change password" class="btn btn-default" [disabled]="!changePasswordForm.valid">
+</form>
--- /dev/null
+import { Control, ControlGroup, Validators } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { AccountService } from './account.service';
+
+@Component({
+ selector: 'my-account',
+ template: require('./account.component.html'),
+ providers: [ AccountService ]
+})
+
+export class AccountComponent implements OnInit {
+ changePasswordForm: ControlGroup;
+ information: string = null;
+ error: string = null;
+
+ constructor(
+ private accountService: AccountService,
+ private router: Router
+ ) {}
+
+ ngOnInit() {
+ this.changePasswordForm = new ControlGroup({
+ newPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])),
+ newConfirmedPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])),
+ });
+ }
+
+ changePassword(newPassword: string, newConfirmedPassword: string) {
+ this.information = null;
+ this.error = null;
+
+ if (newPassword !== newConfirmedPassword) {
+ this.error = 'The new password and the confirmed password do not correspond.';
+ return;
+ }
+
+ this.accountService.changePassword(newPassword).subscribe(
+ ok => this.information = 'Password updated.',
+
+ err => this.error = err
+ );
+ }
+}
--- /dev/null
+import { AccountComponent } from './account.component';
+
+export const AccountRoutes = [
+ { path: 'account', component: AccountComponent }
+];
--- /dev/null
+import { Injectable } from '@angular/core';
+
+import { AuthHttp, AuthService } from '../shared';
+
+@Injectable()
+export class AccountService {
+ private static BASE_USERS_URL = '/api/v1/users/';
+
+ constructor(private authHttp: AuthHttp, private authService: AuthService) { }
+
+ changePassword(newPassword: string) {
+ const url = AccountService.BASE_USERS_URL + this.authService.getUser().id;
+ const body = {
+ password: newPassword
+ };
+
+ return this.authHttp.put(url, body);
+ }
+}
--- /dev/null
+export * from './account.component';
+export * from './account.routes';
<menu class="col-md-2 col-sm-3 col-xs-3">
<div class="panel-block">
<div id="panel-user-login" class="panel-button">
+ <span *ngIf="!isLoggedIn" >
+ <span class="hidden-xs glyphicon glyphicon-log-in"></span>
+ <a [routerLink]="['/login']">Login</a>
+ </span>
+
+ <span *ngIf="isLoggedIn">
+ <span class="hidden-xs glyphicon glyphicon-log-out"></span>
+ <a *ngIf="isLoggedIn" (click)="logout()">Logout</a>
+ </span>
+ </div>
+
+ <div *ngIf="isLoggedIn" id="panel-user-account" class="panel-button">
<span class="hidden-xs glyphicon glyphicon-user"></span>
- <a *ngIf="!isLoggedIn" [routerLink]="['/login']">Login</a>
- <a *ngIf="isLoggedIn" (click)="logout()">Logout</a>
+ <a [routerLink]="['/account']">My account</a>
</div>
</div>
import { RouterConfig } from '@angular/router';
+import { AccountRoutes } from './account';
import { LoginRoutes } from './login';
import { VideosRoutes } from './videos';
pathMatch: 'full'
},
+ ...AccountRoutes,
...LoginRoutes,
...VideosRoutes
];
return this.request(url, options);
}
- post(url: string, options?: RequestOptionsArgs): Observable<Response> {
+ post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
if (!options) options = {};
options.method = RequestMethod.Post;
+ options.body = body;
return this.request(url, options);
}
- put(url: string, options?: RequestOptionsArgs): Observable<Response> {
+ put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
if (!options) options = {};
options.method = RequestMethod.Put;
+ options.body = body;
return this.request(url, options);
}
export class AuthService {
private static BASE_CLIENT_URL = '/api/v1/clients/local';
private static BASE_TOKEN_URL = '/api/v1/users/token';
+ private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me';
loginChangedSource: Observable<AuthStatus>;
res.username = username;
return res;
})
+ .flatMap(res => this.fetchUserInformations(res))
.map(res => this.handleLogin(res))
.catch(this.handleError);
}
.catch(this.handleError);
}
- private setStatus(status: AuthStatus) {
- this.loginChanged.next(status);
+ private fetchUserInformations (obj: any) {
+ // Do not call authHttp here to avoid circular dependencies headaches
+
+ const headers = new Headers();
+ headers.set('Authorization', `Bearer ${obj.access_token}`);
+
+ return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
+ .map(res => res.json())
+ .map(res => {
+ obj.id = res.id;
+ obj.role = res.role;
+ return obj;
+ }
+ );
+ }
+
+ private handleError (error: Response) {
+ console.error(error);
+ return Observable.throw(error.json() || { error: 'Server error' });
}
private handleLogin (obj: any) {
+ const id = obj.id;
const username = obj.username;
+ const role = obj.role;
const hash_tokens = {
access_token: obj.access_token,
token_type: obj.token_type,
refresh_token: obj.refresh_token
};
- this.user = new User(username, hash_tokens);
+ this.user = new User(id, username, role, hash_tokens);
this.user.save();
this.setStatus(AuthStatus.LoggedIn);
}
- private handleError (error: Response) {
- console.error(error);
- return Observable.throw(error.json() || { error: 'Server error' });
- }
-
private handleRefreshToken (obj: any) {
this.user.refreshTokens(obj.access_token, obj.refresh_token);
this.user.save();
}
+
+ private setStatus(status: AuthStatus) {
+ this.loginChanged.next(status);
+ }
+
}
export class User {
private static KEYS = {
+ ID: 'id',
+ ROLE: 'role',
USERNAME: 'username'
};
+ id: string;
+ role: string;
username: string;
tokens: Tokens;
static load() {
const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME);
if (usernameLocalStorage) {
- return new User(localStorage.getItem(this.KEYS.USERNAME), Tokens.load());
+ return new User(
+ localStorage.getItem(this.KEYS.ID),
+ localStorage.getItem(this.KEYS.USERNAME),
+ localStorage.getItem(this.KEYS.ROLE),
+ Tokens.load()
+ );
}
return null;
static flush() {
localStorage.removeItem(this.KEYS.USERNAME);
+ localStorage.removeItem(this.KEYS.ID);
+ localStorage.removeItem(this.KEYS.ROLE);
Tokens.flush();
}
- constructor(username: string, hash_tokens: any) {
+ constructor(id: string, username: string, role: string, hash_tokens: any) {
+ this.id = id;
this.username = username;
+ this.role = role;
this.tokens = new Tokens(hash_tokens);
}
}
save() {
- localStorage.setItem('username', this.username);
+ localStorage.setItem(User.KEYS.ID, this.id);
+ localStorage.setItem(User.KEYS.USERNAME, this.username);
+ localStorage.setItem(User.KEYS.ROLE, this.role);
this.tokens.save();
}
}
-// Private class used only by User
+// Private class only used by User
class Tokens {
private static KEYS = {
ACCESS_TOKEN: 'access_token',
"typings/main.d.ts"
],
"files": [
+ "src/app/account/account.component.ts",
+ "src/app/account/account.routes.ts",
+ "src/app/account/account.service.ts",
+ "src/app/account/index.ts",
"src/app/app.component.ts",
"src/app/app.routes.ts",
"src/app/friends/friend.service.ts",
"src/app/shared/search/search.component.ts",
"src/app/shared/search/search.model.ts",
"src/app/shared/search/search.service.ts",
+ "src/app/shared/user/index.ts",
+ "src/app/shared/user/user.service.ts",
"src/app/videos/index.ts",
"src/app/videos/shared/index.ts",
"src/app/videos/shared/loader/index.ts",