aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/auth/auth-http.service.ts19
-rw-r--r--client/src/app/shared/auth/auth-user.model.ts (renamed from client/src/app/shared/auth/user.model.ts)31
-rw-r--r--client/src/app/shared/auth/auth.service.ts94
-rw-r--r--client/src/app/shared/auth/index.ts2
-rw-r--r--client/src/app/shared/forms/form-reactive.ts24
-rw-r--r--client/src/app/shared/forms/form-validators/index.ts3
-rw-r--r--client/src/app/shared/forms/form-validators/url.validator.ts11
-rw-r--r--client/src/app/shared/forms/form-validators/user.ts17
-rw-r--r--client/src/app/shared/forms/form-validators/video.ts25
-rw-r--r--client/src/app/shared/forms/index.ts2
-rw-r--r--client/src/app/shared/index.ts3
-rw-r--r--client/src/app/shared/rest/index.ts3
-rw-r--r--client/src/app/shared/rest/rest-extractor.service.ts52
-rw-r--r--client/src/app/shared/rest/rest-pagination.ts5
-rw-r--r--client/src/app/shared/rest/rest.service.ts27
-rw-r--r--client/src/app/shared/search/search.component.ts16
-rw-r--r--client/src/app/shared/search/search.service.ts3
-rw-r--r--client/src/app/shared/users/index.ts1
-rw-r--r--client/src/app/shared/users/user.model.ts20
19 files changed, 310 insertions, 48 deletions
diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts
index 9c7ef4389..2392898ca 100644
--- a/client/src/app/shared/auth/auth-http.service.ts
+++ b/client/src/app/shared/auth/auth-http.service.ts
@@ -28,7 +28,7 @@ export class AuthHttp extends Http {
28 return super.request(url, options) 28 return super.request(url, options)
29 .catch((err) => { 29 .catch((err) => {
30 if (err.status === 401) { 30 if (err.status === 401) {
31 return this.handleTokenExpired(err, url, options); 31 return this.handleTokenExpired(url, options);
32 } 32 }
33 33
34 return Observable.throw(err); 34 return Observable.throw(err);
@@ -49,26 +49,29 @@ export class AuthHttp extends Http {
49 return this.request(url, options); 49 return this.request(url, options);
50 } 50 }
51 51
52 post(url: string, options?: RequestOptionsArgs): Observable<Response> { 52 post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
53 if (!options) options = {}; 53 if (!options) options = {};
54 options.method = RequestMethod.Post; 54 options.method = RequestMethod.Post;
55 options.body = body;
55 56
56 return this.request(url, options); 57 return this.request(url, options);
57 } 58 }
58 59
59 put(url: string, options?: RequestOptionsArgs): Observable<Response> { 60 put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
60 if (!options) options = {}; 61 if (!options) options = {};
61 options.method = RequestMethod.Put; 62 options.method = RequestMethod.Put;
63 options.body = body;
62 64
63 return this.request(url, options); 65 return this.request(url, options);
64 } 66 }
65 67
66 private handleTokenExpired(err: Response, url: string | Request, options: RequestOptionsArgs) { 68 private handleTokenExpired(url: string | Request, options: RequestOptionsArgs) {
67 return this.authService.refreshAccessToken().flatMap(() => { 69 return this.authService.refreshAccessToken()
68 this.setAuthorizationHeader(options.headers); 70 .flatMap(() => {
71 this.setAuthorizationHeader(options.headers);
69 72
70 return super.request(url, options); 73 return super.request(url, options);
71 }); 74 });
72 } 75 }
73 76
74 private setAuthorizationHeader(headers: Headers) { 77 private setAuthorizationHeader(headers: Headers) {
diff --git a/client/src/app/shared/auth/user.model.ts b/client/src/app/shared/auth/auth-user.model.ts
index 98852f835..bdd5ea5a9 100644
--- a/client/src/app/shared/auth/user.model.ts
+++ b/client/src/app/shared/auth/auth-user.model.ts
@@ -1,15 +1,28 @@
1export class User { 1import { User } from '../users';
2
3export class AuthUser extends User {
2 private static KEYS = { 4 private static KEYS = {
5 ID: 'id',
6 ROLE: 'role',
3 USERNAME: 'username' 7 USERNAME: 'username'
4 }; 8 };
5 9
10 id: string;
11 role: string;
6 username: string; 12 username: string;
7 tokens: Tokens; 13 tokens: Tokens;
8 14
9 static load() { 15 static load() {
10 const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME); 16 const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME);
11 if (usernameLocalStorage) { 17 if (usernameLocalStorage) {
12 return new User(localStorage.getItem(this.KEYS.USERNAME), Tokens.load()); 18 return new AuthUser(
19 {
20 id: localStorage.getItem(this.KEYS.ID),
21 username: localStorage.getItem(this.KEYS.USERNAME),
22 role: localStorage.getItem(this.KEYS.ROLE)
23 },
24 Tokens.load()
25 );
13 } 26 }
14 27
15 return null; 28 return null;
@@ -17,12 +30,14 @@ export class User {
17 30
18 static flush() { 31 static flush() {
19 localStorage.removeItem(this.KEYS.USERNAME); 32 localStorage.removeItem(this.KEYS.USERNAME);
33 localStorage.removeItem(this.KEYS.ID);
34 localStorage.removeItem(this.KEYS.ROLE);
20 Tokens.flush(); 35 Tokens.flush();
21 } 36 }
22 37
23 constructor(username: string, hash_tokens: any) { 38 constructor(userHash: { id: string, username: string, role: string }, hashTokens: any) {
24 this.username = username; 39 super(userHash);
25 this.tokens = new Tokens(hash_tokens); 40 this.tokens = new Tokens(hashTokens);
26 } 41 }
27 42
28 getAccessToken() { 43 getAccessToken() {
@@ -43,12 +58,14 @@ export class User {
43 } 58 }
44 59
45 save() { 60 save() {
46 localStorage.setItem('username', this.username); 61 localStorage.setItem(AuthUser.KEYS.ID, this.id);
62 localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
63 localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
47 this.tokens.save(); 64 this.tokens.save();
48 } 65 }
49} 66}
50 67
51// Private class used only by User 68// Private class only used by User
52class Tokens { 69class Tokens {
53 private static KEYS = { 70 private static KEYS = {
54 ACCESS_TOKEN: 'access_token', 71 ACCESS_TOKEN: 'access_token',
diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts
index 584298fff..a30c79c86 100644
--- a/client/src/app/shared/auth/auth.service.ts
+++ b/client/src/app/shared/auth/auth.service.ts
@@ -1,32 +1,39 @@
1import { Injectable } from '@angular/core'; 1import { Injectable } from '@angular/core';
2import { Headers, Http, Response, URLSearchParams } from '@angular/http'; 2import { Headers, Http, Response, URLSearchParams } from '@angular/http';
3import { Router } from '@angular/router';
3import { Observable } from 'rxjs/Observable'; 4import { Observable } from 'rxjs/Observable';
4import { Subject } from 'rxjs/Subject'; 5import { Subject } from 'rxjs/Subject';
5 6
6import { AuthStatus } from './auth-status.model'; 7import { AuthStatus } from './auth-status.model';
7import { User } from './user.model'; 8import { AuthUser } from './auth-user.model';
9import { RestExtractor } from '../rest';
8 10
9@Injectable() 11@Injectable()
10export class AuthService { 12export class AuthService {
11 private static BASE_CLIENT_URL = '/api/v1/users/client'; 13 private static BASE_CLIENT_URL = '/api/v1/clients/local';
12 private static BASE_TOKEN_URL = '/api/v1/users/token'; 14 private static BASE_TOKEN_URL = '/api/v1/users/token';
15 private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me';
13 16
14 loginChangedSource: Observable<AuthStatus>; 17 loginChangedSource: Observable<AuthStatus>;
15 18
16 private clientId: string; 19 private clientId: string;
17 private clientSecret: string; 20 private clientSecret: string;
18 private loginChanged: Subject<AuthStatus>; 21 private loginChanged: Subject<AuthStatus>;
19 private user: User = null; 22 private user: AuthUser = null;
20 23
21 constructor(private http: Http) { 24 constructor(
25 private http: Http,
26 private restExtractor: RestExtractor,
27 private router: Router
28 ) {
22 this.loginChanged = new Subject<AuthStatus>(); 29 this.loginChanged = new Subject<AuthStatus>();
23 this.loginChangedSource = this.loginChanged.asObservable(); 30 this.loginChangedSource = this.loginChanged.asObservable();
24 31
25 // Fetch the client_id/client_secret 32 // Fetch the client_id/client_secret
26 // FIXME: save in local storage? 33 // FIXME: save in local storage?
27 this.http.get(AuthService.BASE_CLIENT_URL) 34 this.http.get(AuthService.BASE_CLIENT_URL)
28 .map(res => res.json()) 35 .map(this.restExtractor.extractDataGet)
29 .catch(this.handleError) 36 .catch((res) => this.restExtractor.handleError(res))
30 .subscribe( 37 .subscribe(
31 result => { 38 result => {
32 this.clientId = result.client_id; 39 this.clientId = result.client_id;
@@ -34,12 +41,15 @@ export class AuthService {
34 console.log('Client credentials loaded.'); 41 console.log('Client credentials loaded.');
35 }, 42 },
36 error => { 43 error => {
37 alert(error); 44 alert(
45 `Cannot retrieve OAuth Client credentials: ${error.text}. \n` +
46 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
47 );
38 } 48 }
39 ); 49 );
40 50
41 // Return null if there is nothing to load 51 // Return null if there is nothing to load
42 this.user = User.load(); 52 this.user = AuthUser.load();
43 } 53 }
44 54
45 getRefreshToken() { 55 getRefreshToken() {
@@ -64,10 +74,16 @@ export class AuthService {
64 return this.user.getTokenType(); 74 return this.user.getTokenType();
65 } 75 }
66 76
67 getUser(): User { 77 getUser(): AuthUser {
68 return this.user; 78 return this.user;
69 } 79 }
70 80
81 isAdmin() {
82 if (this.user === null) return false;
83
84 return this.user.isAdmin();
85 }
86
71 isLoggedIn() { 87 isLoggedIn() {
72 if (this.getAccessToken()) { 88 if (this.getAccessToken()) {
73 return true; 89 return true;
@@ -94,21 +110,23 @@ export class AuthService {
94 }; 110 };
95 111
96 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) 112 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
97 .map(res => res.json()) 113 .map(this.restExtractor.extractDataGet)
98 .map(res => { 114 .map(res => {
99 res.username = username; 115 res.username = username;
100 return res; 116 return res;
101 }) 117 })
118 .flatMap(res => this.fetchUserInformations(res))
102 .map(res => this.handleLogin(res)) 119 .map(res => this.handleLogin(res))
103 .catch(this.handleError); 120 .catch((res) => this.restExtractor.handleError(res));
104 } 121 }
105 122
106 logout() { 123 logout() {
107 // TODO: make an HTTP request to revoke the tokens 124 // TODO: make an HTTP request to revoke the tokens
108 this.user = null; 125 this.user = null;
109 User.flush();
110 126
111 this.setStatus(AuthStatus.LoggedIn); 127 AuthUser.flush();
128
129 this.setStatus(AuthStatus.LoggedOut);
112 } 130 }
113 131
114 refreshAccessToken() { 132 refreshAccessToken() {
@@ -131,36 +149,64 @@ export class AuthService {
131 }; 149 };
132 150
133 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) 151 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
134 .map(res => res.json()) 152 .map(this.restExtractor.extractDataGet)
135 .map(res => this.handleRefreshToken(res)) 153 .map(res => this.handleRefreshToken(res))
136 .catch(this.handleError); 154 .catch((res: Response) => {
155 // The refresh token is invalid?
156 if (res.status === 400 && res.json() && res.json().error === 'invalid_grant') {
157 console.error('Cannot refresh token -> logout...');
158 this.logout();
159 this.router.navigate(['/login']);
160
161 return Observable.throw({
162 json: '',
163 text: 'You need to reconnect.'
164 });
165 }
166
167 return this.restExtractor.handleError(res);
168 });
137 } 169 }
138 170
139 private setStatus(status: AuthStatus) { 171 private fetchUserInformations (obj: any) {
140 this.loginChanged.next(status); 172 // Do not call authHttp here to avoid circular dependencies headaches
173
174 const headers = new Headers();
175 headers.set('Authorization', `Bearer ${obj.access_token}`);
176
177 return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
178 .map(res => res.json())
179 .map(res => {
180 obj.id = res.id;
181 obj.role = res.role;
182 return obj;
183 }
184 );
141 } 185 }
142 186
143 private handleLogin (obj: any) { 187 private handleLogin (obj: any) {
188 const id = obj.id;
144 const username = obj.username; 189 const username = obj.username;
145 const hash_tokens = { 190 const role = obj.role;
191 const hashTokens = {
146 access_token: obj.access_token, 192 access_token: obj.access_token,
147 token_type: obj.token_type, 193 token_type: obj.token_type,
148 refresh_token: obj.refresh_token 194 refresh_token: obj.refresh_token
149 }; 195 };
150 196
151 this.user = new User(username, hash_tokens); 197 this.user = new AuthUser({ id, username, role }, hashTokens);
152 this.user.save(); 198 this.user.save();
153 199
154 this.setStatus(AuthStatus.LoggedIn); 200 this.setStatus(AuthStatus.LoggedIn);
155 } 201 }
156 202
157 private handleError (error: Response) {
158 console.error(error);
159 return Observable.throw(error.json() || { error: 'Server error' });
160 }
161
162 private handleRefreshToken (obj: any) { 203 private handleRefreshToken (obj: any) {
163 this.user.refreshTokens(obj.access_token, obj.refresh_token); 204 this.user.refreshTokens(obj.access_token, obj.refresh_token);
164 this.user.save(); 205 this.user.save();
165 } 206 }
207
208 private setStatus(status: AuthStatus) {
209 this.loginChanged.next(status);
210 }
211
166} 212}
diff --git a/client/src/app/shared/auth/index.ts b/client/src/app/shared/auth/index.ts
index aafaacbf1..ebd9e14cd 100644
--- a/client/src/app/shared/auth/index.ts
+++ b/client/src/app/shared/auth/index.ts
@@ -1,4 +1,4 @@
1export * from './auth-http.service'; 1export * from './auth-http.service';
2export * from './auth-status.model'; 2export * from './auth-status.model';
3export * from './auth.service'; 3export * from './auth.service';
4export * from './user.model'; 4export * from './auth-user.model';
diff --git a/client/src/app/shared/forms/form-reactive.ts b/client/src/app/shared/forms/form-reactive.ts
new file mode 100644
index 000000000..1e8a69771
--- /dev/null
+++ b/client/src/app/shared/forms/form-reactive.ts
@@ -0,0 +1,24 @@
1import { FormGroup } from '@angular/forms';
2
3export abstract class FormReactive {
4 abstract form: FormGroup;
5 abstract formErrors: Object;
6 abstract validationMessages: Object;
7
8 abstract buildForm(): void;
9
10 protected onValueChanged(data?: any) {
11 for (const field in this.formErrors) {
12 // clear previous error message (if any)
13 this.formErrors[field] = '';
14 const control = this.form.get(field);
15
16 if (control && control.dirty && !control.valid) {
17 const messages = this.validationMessages[field];
18 for (const key in control.errors) {
19 this.formErrors[field] += messages[key] + ' ';
20 }
21 }
22 }
23 }
24}
diff --git a/client/src/app/shared/forms/form-validators/index.ts b/client/src/app/shared/forms/form-validators/index.ts
new file mode 100644
index 000000000..1d2ae6f68
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/index.ts
@@ -0,0 +1,3 @@
1export * from './url.validator';
2export * from './user';
3export * from './video';
diff --git a/client/src/app/shared/forms/form-validators/url.validator.ts b/client/src/app/shared/forms/form-validators/url.validator.ts
new file mode 100644
index 000000000..67163b4e9
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/url.validator.ts
@@ -0,0 +1,11 @@
1import { FormControl } from '@angular/forms';
2
3export function validateUrl(c: FormControl) {
4 let URL_REGEXP = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$');
5
6 return URL_REGEXP.test(c.value) ? null : {
7 validateUrl: {
8 valid: false
9 }
10 };
11}
diff --git a/client/src/app/shared/forms/form-validators/user.ts b/client/src/app/shared/forms/form-validators/user.ts
new file mode 100644
index 000000000..5b11ff265
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/user.ts
@@ -0,0 +1,17 @@
1import { Validators } from '@angular/forms';
2
3export const USER_USERNAME = {
4 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(20) ],
5 MESSAGES: {
6 'required': 'Username is required.',
7 'minlength': 'Username must be at least 3 characters long.',
8 'maxlength': 'Username cannot be more than 20 characters long.'
9 }
10};
11export const USER_PASSWORD = {
12 VALIDATORS: [ Validators.required, Validators.minLength(6) ],
13 MESSAGES: {
14 'required': 'Password is required.',
15 'minlength': 'Password must be at least 6 characters long.',
16 }
17};
diff --git a/client/src/app/shared/forms/form-validators/video.ts b/client/src/app/shared/forms/form-validators/video.ts
new file mode 100644
index 000000000..3766d4018
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/video.ts
@@ -0,0 +1,25 @@
1import { Validators } from '@angular/forms';
2
3export const VIDEO_NAME = {
4 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(50) ],
5 MESSAGES: {
6 'required': 'Video name is required.',
7 'minlength': 'Video name must be at least 3 characters long.',
8 'maxlength': 'Video name cannot be more than 50 characters long.'
9 }
10};
11export const VIDEO_DESCRIPTION = {
12 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(250) ],
13 MESSAGES: {
14 'required': 'Video description is required.',
15 'minlength': 'Video description must be at least 3 characters long.',
16 'maxlength': 'Video description cannot be more than 250 characters long.'
17 }
18};
19
20export const VIDEO_TAGS = {
21 VALIDATORS: [ Validators.pattern('^[a-zA-Z0-9]{2,10}$') ],
22 MESSAGES: {
23 'pattern': 'A tag should be between 2 and 10 alphanumeric characters long.'
24 }
25};
diff --git a/client/src/app/shared/forms/index.ts b/client/src/app/shared/forms/index.ts
new file mode 100644
index 000000000..588ebb4be
--- /dev/null
+++ b/client/src/app/shared/forms/index.ts
@@ -0,0 +1,2 @@
1export * from './form-validators';
2export * from './form-reactive';
diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts
index dfea4c67c..af34b4b64 100644
--- a/client/src/app/shared/index.ts
+++ b/client/src/app/shared/index.ts
@@ -1,2 +1,5 @@
1export * from './auth'; 1export * from './auth';
2export * from './forms';
3export * from './rest';
2export * from './search'; 4export * from './search';
5export * from './users';
diff --git a/client/src/app/shared/rest/index.ts b/client/src/app/shared/rest/index.ts
new file mode 100644
index 000000000..3c9509dc7
--- /dev/null
+++ b/client/src/app/shared/rest/index.ts
@@ -0,0 +1,3 @@
1export * from './rest-extractor.service';
2export * from './rest-pagination';
3export * from './rest.service';
diff --git a/client/src/app/shared/rest/rest-extractor.service.ts b/client/src/app/shared/rest/rest-extractor.service.ts
new file mode 100644
index 000000000..fcb1598f4
--- /dev/null
+++ b/client/src/app/shared/rest/rest-extractor.service.ts
@@ -0,0 +1,52 @@
1import { Injectable } from '@angular/core';
2import { Response } from '@angular/http';
3import { Observable } from 'rxjs/Observable';
4
5export interface ResultList {
6 data: any[];
7 total: number;
8}
9
10@Injectable()
11export class RestExtractor {
12
13 constructor () { ; }
14
15 extractDataBool(res: Response) {
16 return true;
17 }
18
19 extractDataList(res: Response) {
20 const body = res.json();
21
22 const ret: ResultList = {
23 data: body.data,
24 total: body.total
25 };
26
27 return ret;
28 }
29
30 extractDataGet(res: Response) {
31 return res.json();
32 }
33
34 handleError(res: Response) {
35 let text = 'Server error: ';
36 text += res.text();
37 let json = '';
38
39 try {
40 json = res.json();
41 } catch (err) { ; }
42
43 const error = {
44 json,
45 text
46 };
47
48 console.error(error);
49
50 return Observable.throw(error);
51 }
52}
diff --git a/client/src/app/shared/rest/rest-pagination.ts b/client/src/app/shared/rest/rest-pagination.ts
new file mode 100644
index 000000000..0cfa4f468
--- /dev/null
+++ b/client/src/app/shared/rest/rest-pagination.ts
@@ -0,0 +1,5 @@
1export interface RestPagination {
2 currentPage: number;
3 itemsPerPage: number;
4 totalItems: number;
5};
diff --git a/client/src/app/shared/rest/rest.service.ts b/client/src/app/shared/rest/rest.service.ts
new file mode 100644
index 000000000..16b47e957
--- /dev/null
+++ b/client/src/app/shared/rest/rest.service.ts
@@ -0,0 +1,27 @@
1import { Injectable } from '@angular/core';
2import { URLSearchParams } from '@angular/http';
3
4import { RestPagination } from './rest-pagination';
5
6@Injectable()
7export class RestService {
8
9 buildRestGetParams(pagination?: RestPagination, sort?: string) {
10 const params = new URLSearchParams();
11
12 if (pagination) {
13 const start: number = (pagination.currentPage - 1) * pagination.itemsPerPage;
14 const count: number = pagination.itemsPerPage;
15
16 params.set('start', start.toString());
17 params.set('count', count.toString());
18 }
19
20 if (sort) {
21 params.set('sort', sort);
22 }
23
24 return params;
25 }
26
27}
diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts
index e864fbc17..b6237469b 100644
--- a/client/src/app/shared/search/search.component.ts
+++ b/client/src/app/shared/search/search.component.ts
@@ -1,15 +1,13 @@
1import { Component, OnInit } from '@angular/core'; 1import { Component, OnInit } from '@angular/core';
2 2import { Router } from '@angular/router';
3import { DROPDOWN_DIRECTIVES} from 'ng2-bootstrap/components/dropdown';
4 3
5import { Search } from './search.model'; 4import { Search } from './search.model';
6import { SearchField } from './search-field.type'; 5import { SearchField } from './search-field.type';
7import { SearchService } from './search.service'; 6import { SearchService } from './search.service';
8 7
9@Component({ 8@Component({
10 selector: 'my-search', 9 selector: 'my-search',
11 template: require('./search.component.html'), 10 templateUrl: './search.component.html'
12 directives: [ DROPDOWN_DIRECTIVES ]
13}) 11})
14 12
15export class SearchComponent implements OnInit { 13export class SearchComponent implements OnInit {
@@ -25,10 +23,10 @@ export class SearchComponent implements OnInit {
25 value: '' 23 value: ''
26 }; 24 };
27 25
28 constructor(private searchService: SearchService) {} 26 constructor(private searchService: SearchService, private router: Router) {}
29 27
30 ngOnInit() { 28 ngOnInit() {
31 // Subscribe is the search changed 29 // Subscribe if the search changed
32 // Usually changed by videos list component 30 // Usually changed by videos list component
33 this.searchService.updateSearch.subscribe( 31 this.searchService.updateSearch.subscribe(
34 newSearchCriterias => { 32 newSearchCriterias => {
@@ -58,6 +56,10 @@ export class SearchComponent implements OnInit {
58 } 56 }
59 57
60 doSearch() { 58 doSearch() {
59 if (this.router.url.indexOf('/videos/list') === -1) {
60 this.router.navigate([ '/videos/list' ]);
61 }
62
61 this.searchService.searchUpdated.next(this.searchCriterias); 63 this.searchService.searchUpdated.next(this.searchCriterias);
62 } 64 }
63 65
diff --git a/client/src/app/shared/search/search.service.ts b/client/src/app/shared/search/search.service.ts
index c7993db3d..717a7fa50 100644
--- a/client/src/app/shared/search/search.service.ts
+++ b/client/src/app/shared/search/search.service.ts
@@ -1,5 +1,6 @@
1import { Injectable } from '@angular/core'; 1import { Injectable } from '@angular/core';
2import { Subject } from 'rxjs/Subject'; 2import { Subject } from 'rxjs/Subject';
3import { ReplaySubject } from 'rxjs/ReplaySubject';
3 4
4import { Search } from './search.model'; 5import { Search } from './search.model';
5 6
@@ -12,6 +13,6 @@ export class SearchService {
12 13
13 constructor() { 14 constructor() {
14 this.updateSearch = new Subject<Search>(); 15 this.updateSearch = new Subject<Search>();
15 this.searchUpdated = new Subject<Search>(); 16 this.searchUpdated = new ReplaySubject<Search>(1);
16 } 17 }
17} 18}
diff --git a/client/src/app/shared/users/index.ts b/client/src/app/shared/users/index.ts
new file mode 100644
index 000000000..5a670ce8f
--- /dev/null
+++ b/client/src/app/shared/users/index.ts
@@ -0,0 +1 @@
export * from './user.model';
diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts
new file mode 100644
index 000000000..726495d11
--- /dev/null
+++ b/client/src/app/shared/users/user.model.ts
@@ -0,0 +1,20 @@
1export class User {
2 id: string;
3 username: string;
4 role: string;
5 createdDate: Date;
6
7 constructor(hash: { id: string, username: string, role: string, createdDate?: Date }) {
8 this.id = hash.id;
9 this.username = hash.username;
10 this.role = hash.role;
11
12 if (hash.createdDate) {
13 this.createdDate = hash.createdDate;
14 }
15 }
16
17 isAdmin() {
18 return this.role === 'admin';
19 }
20}