Commit 33647b984dd508f4f2ec493305971379222e0de3
1 parent
afb4744a
Exists in
forgot_password
Ticket #107 Implementation of 'Forgot password'
Showing
9 changed files
with
129 additions
and
17 deletions
Show diff stats
src/app/login/auth.controller.spec.ts
1 | 1 | import {AuthController} from "./auth.controller"; |
2 | 2 | import {AuthService} from "./auth.service"; |
3 | +import * as helpers from "./../../spec/helpers"; | |
3 | 4 | |
4 | 5 | describe("Controllers", () => { |
5 | 6 | |
6 | 7 | |
7 | 8 | describe("AuthController", () => { |
8 | 9 | |
9 | - it("calls authenticate on AuthService when login called", () => { | |
10 | + let $modal: any; | |
11 | + let notificationService = helpers.mocks.notificationService; | |
12 | + notificationService.success = jasmine.createSpy('info'); | |
13 | + notificationService.error = jasmine.createSpy('error'); | |
10 | 14 | |
11 | - // creating a Mock AuthService | |
12 | - let AuthServiceMock: AuthService = jasmine.createSpyObj("AuthService", ["login"]); | |
15 | + let authController: any; | |
16 | + let AuthServiceMock: AuthService; | |
17 | + let username: string; | |
13 | 18 | |
14 | - // pass AuthServiceMock into the constructor | |
15 | - let authController = new AuthController(null, null, AuthServiceMock); | |
19 | + beforeEach(() => { | |
20 | + $modal = helpers.mocks.$modal; | |
21 | + AuthServiceMock = jasmine.createSpyObj("AuthService", ["login", | |
22 | +"forgotPassword"]); | |
23 | + authController = new AuthController(null, null, AuthServiceMock, $modal, notificationService); | |
24 | + }); | |
16 | 25 | |
17 | - // setup of authController -> set the credentials instance property | |
18 | - let credentials = { username: "username", password: "password" }; | |
19 | 26 | |
27 | + it("calls authenticate on AuthService when login called", () => { | |
28 | + let credentials = { username: "username", password: "password" }; | |
20 | 29 | authController.credentials = credentials; |
21 | - | |
22 | - // calls the authController login method | |
23 | 30 | authController.login(); |
24 | - | |
25 | - // checks if the method login of the injected AuthService has been called | |
26 | 31 | expect(AuthServiceMock.login).toHaveBeenCalledWith(credentials); |
27 | - | |
28 | 32 | }); |
29 | 33 | |
34 | + it('should open forgot password on click', (done: Function) => { | |
35 | + spyOn($modal, "open"); | |
36 | + authController.openForgotPassword(); | |
37 | + expect($modal.open).toHaveBeenCalled(); | |
38 | + expect($modal.open).toHaveBeenCalledWith({ | |
39 | + templateUrl: 'app/login/forgot-password.html', | |
40 | + controller: AuthController, | |
41 | + controllerAs: 'vm', | |
42 | + bindToController: true | |
43 | + }); | |
44 | + done(); | |
45 | + }); | |
30 | 46 | |
47 | + it("calls forgotPassword on AuthService when sendPasswdInfo called", done => { | |
48 | + authController.username = "john"; | |
49 | + AuthServiceMock.forgotPassword = jasmine.createSpy("forgotPassword").and.returnValue(Promise.resolve()); | |
50 | + authController.sendPasswdInfo(); | |
51 | + expect(AuthServiceMock.forgotPassword).toHaveBeenCalledWith("john"); | |
52 | + done(); | |
53 | + }); | |
31 | 54 | |
32 | 55 | }); |
33 | 56 | }); | ... | ... |
src/app/login/auth.controller.ts
1 | -import {AuthService} from "./auth.service"; | |
1 | +import { AuthService } from "./auth.service"; | |
2 | +import { NotificationService } from "./../shared/services/notification.service"; | |
2 | 3 | |
3 | 4 | export class AuthController { |
4 | 5 | |
5 | - static $inject = ["$log", "$stateParams", "AuthService"]; | |
6 | + static $inject = ["$log", "$stateParams", "AuthService", "$uibModal", "NotificationService"]; | |
7 | + modalInstance: ng.ui.bootstrap.IModalServiceInstance; | |
6 | 8 | |
7 | 9 | constructor( |
8 | 10 | private $log: ng.ILogService, |
9 | 11 | private $stateParams: any, |
10 | - private AuthService: AuthService | |
12 | + private AuthService: AuthService, | |
13 | + private $uibModal: ng.ui.bootstrap.IModalService, | |
14 | + private notificationService: NotificationService | |
11 | 15 | ) { |
12 | 16 | |
13 | 17 | } |
14 | 18 | |
15 | 19 | credentials: noosfero.Credentials; |
20 | + username: string; | |
16 | 21 | |
17 | 22 | login() { |
18 | 23 | this.AuthService.login(this.credentials); |
19 | 24 | } |
25 | + | |
26 | + openForgotPassword() { | |
27 | + this.modalInstance = this.$uibModal.open({ | |
28 | + templateUrl: 'app/login/forgot-password.html', | |
29 | + controller: AuthController, | |
30 | + controllerAs: 'vm', | |
31 | + bindToController: true, | |
32 | + }); | |
33 | + } | |
34 | + | |
35 | + sendPasswdInfo() { | |
36 | + this.AuthService.forgotPassword(this.username).then((response) => { | |
37 | + this.notificationService.info({ title: "forgotPasswd.email_sent.title.info", message: "forgotPasswd.email_sent.message.info" }); | |
38 | + }).catch((response) => { | |
39 | + this.notificationService.error({ title: "forgotPasswd.not_found.title.error", message: "forgotPasswd.not_found.message.error" }); | |
40 | + this.openForgotPassword(); | |
41 | + }); | |
42 | + } | |
20 | 43 | } | ... | ... |
src/app/login/auth.service.ts
... | ... | @@ -2,11 +2,12 @@ import {Injectable, Inject, EventEmitter} from "ng-forward"; |
2 | 2 | |
3 | 3 | import {NoosferoRootScope, UserResponse} from "./../shared/models/interfaces"; |
4 | 4 | import {SessionService} from "./session.service"; |
5 | +import { RestangularService } from "./../../lib/ng-noosfero-api/http/restangular_service"; | |
5 | 6 | |
6 | 7 | import {AuthEvents} from "./auth-events"; |
7 | 8 | |
8 | 9 | @Injectable() |
9 | -@Inject("$http", SessionService, "$log") | |
10 | +@Inject("$http", SessionService, "$log", "Restangular") | |
10 | 11 | export class AuthService { |
11 | 12 | |
12 | 13 | public loginSuccess: EventEmitter<noosfero.User> = new EventEmitter<noosfero.User>(); |
... | ... | @@ -15,7 +16,10 @@ export class AuthService { |
15 | 16 | |
16 | 17 | constructor(private $http: ng.IHttpService, |
17 | 18 | private sessionService: SessionService, |
18 | - private $log: ng.ILogService) { | |
19 | + private $log: ng.ILogService, | |
20 | + private Restangular: restangular.IService | |
21 | + ) { | |
22 | + this.Restangular = Restangular; | |
19 | 23 | } |
20 | 24 | |
21 | 25 | loginFromCookie() { |
... | ... | @@ -76,4 +80,9 @@ export class AuthService { |
76 | 80 | throw new Error(`The event: ${eventName} not exists`); |
77 | 81 | } |
78 | 82 | } |
83 | + | |
84 | + forgotPassword(value: string): ng.IPromise<noosfero.RestResult<any>> { | |
85 | + return this.Restangular.all("").customPOST("", "forgot_password", {value: value}); | |
86 | + } | |
87 | + | |
79 | 88 | } | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +<div class="modal-header"> | |
2 | + <h3 class="modal-title">{{"auth.form.forgot_passwd" | translate}}</h3> | |
3 | +</div> | |
4 | +<div class="modal-body"> | |
5 | + <form name="forgotPasswdForm"> | |
6 | + <div class="form-group" ng-class="{'has-error': forgotPasswdForm.username.$error.required }"> | |
7 | + <label for="forgotPasswdLogin">{{"auth.form.login" | translate}}</label> | |
8 | + <input type="text" name="username" class="form-control" id="forgotPasswdLogin" placeholder="{{'auth.form.login' | translate}}" ng-model="vm.username" required> | |
9 | + <div class="help-block" ng-messages="forgotPasswdForm.username.$error"> | |
10 | + <div ng-if="forgotPasswdForm.username.$touched"> | |
11 | + <ul class="list-unstyled"> | |
12 | + <li ng-messages-include="app/shared/messages/form-errors.html"></li> | |
13 | + </ul> | |
14 | + </div> | |
15 | + </div> | |
16 | + </div> | |
17 | + <button type="submit" class="btn btn-default btn-block" ng-click="vm.sendPasswdInfo(); $close()" ng-disabled="forgotPasswdForm.$invalid">{{"forgotPasswd.send_instructions_button" | translate}}</button> | |
18 | + </form> | |
19 | + <p>{{"forgotPasswd.info" | translate}}</p> | |
20 | +</div> | ... | ... |
src/app/login/login.html
... | ... | @@ -18,6 +18,9 @@ |
18 | 18 | {{"auth.form.keepLoggedIn" | translate}} |
19 | 19 | </label> |
20 | 20 | </div> |
21 | + <div class="pull-right"> | |
22 | + <a ng-click="vm.openForgotPassword()" href="">{{"auth.form.forgot_passwd" | translate}}</a> | |
23 | + </div> | |
21 | 24 | </div> |
22 | 25 | <button type="submit" class="btn btn-default btn-block" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> |
23 | 26 | </form> | ... | ... |
src/app/login/login.scss
src/app/shared/services/notification.service.ts
... | ... | @@ -14,6 +14,8 @@ export class NotificationService { |
14 | 14 | public static DEFAULT_ERROR_TITLE = "notification.error.default.title"; |
15 | 15 | public static DEFAULT_ERROR_MESSAGE = "notification.error.default.message"; |
16 | 16 | public static DEFAULT_SUCCESS_TIMER = 1000; |
17 | + public static DEFAULT_INFO_TITLE = "notification.info.default.title"; | |
18 | + public static DEFAULT_INFO_MESSAGE = "notification.info.default.message"; | |
17 | 19 | |
18 | 20 | error({ |
19 | 21 | message = NotificationService.DEFAULT_ERROR_MESSAGE, |
... | ... | @@ -40,6 +42,14 @@ export class NotificationService { |
40 | 42 | this.showMessage({ title: title, text: message, showCancelButton: showCancelButton, type: type, closeOnConfirm: false }, confirmationFunction); |
41 | 43 | } |
42 | 44 | |
45 | + info({ | |
46 | + message = NotificationService.DEFAULT_INFO_MESSAGE, | |
47 | + title = NotificationService.DEFAULT_INFO_TITLE, | |
48 | + showConfirmButton = true | |
49 | + } = {}) { | |
50 | + this.showMessage({ title: title, text: message, showConfirmButton: showConfirmButton, type: "info" }); | |
51 | + } | |
52 | + | |
43 | 53 | private showMessage({title, text, type = "success", timer = null, showConfirmButton = true, showCancelButton = false, closeOnConfirm = true}, confirmationFunction: Function = null) { |
44 | 54 | this.$log.debug("Notification message:", title, text, type, this.translatorService.currentLanguage()); |
45 | 55 | this.SweetAlert.swal({ | ... | ... |
src/languages/en.json
... | ... | @@ -34,8 +34,15 @@ |
34 | 34 | "auth.form.login": "Username or Email address", |
35 | 35 | "auth.form.password": "Password", |
36 | 36 | "auth.form.keepLoggedIn": "Keep me logged in", |
37 | + "auth.form.forgot_passwd": "Forgot your password?", | |
37 | 38 | "auth.form.login_button": "Login", |
38 | 39 | "auth.createAccount": "Create account", |
40 | + "forgotPasswd.send_instructions_button": "Send instructions", | |
41 | + "forgotPasswd.info": "Upon clicking on the button above, you'll receive an email with instructions on how to create a new password.", | |
42 | + "forgotPasswd.email_sent.title.info": "Change your password", | |
43 | + "forgotPasswd.email_sent.message.info": "Follow the instructions sent by email to change your password", | |
44 | + "forgotPasswd.not_found.title.error": "User not found", | |
45 | + "forgotPasswd.not_found.message.error": "Could not send password recovery for the user", | |
39 | 46 | "navbar.content_viewer_actions.new_item": "New Item", |
40 | 47 | "navbar.profile_actions.new_item": "New Item", |
41 | 48 | "navbar.content_viewer_actions.new_post": "New Post", |
... | ... | @@ -43,6 +50,8 @@ |
43 | 50 | "navbar.profile_actions.new_discussion": "New Discussion", |
44 | 51 | "notification.error.default.message": "Something went wrong!", |
45 | 52 | "notification.error.default.title": "Oops...", |
53 | + "notification.info.default.message": "", | |
54 | + "notification.info.default.title": "Information", | |
46 | 55 | "notification.profile.not_found": "Page not found", |
47 | 56 | "notification.http_error.401.message": "Unauthorized", |
48 | 57 | "notification.http_error.500.message": "Server error", | ... | ... |
src/languages/pt.json
... | ... | @@ -34,8 +34,15 @@ |
34 | 34 | "auth.form.login": "Nome de usuário ou Email", |
35 | 35 | "auth.form.password": "Senha", |
36 | 36 | "auth.form.keepLoggedIn": "Continuar logado", |
37 | + "auth.form.forgot_passwd": "Esqueceu sua senha?", | |
37 | 38 | "auth.form.login_button": "Login", |
38 | 39 | "auth.createAccount": "Criar conta", |
40 | + "forgotPasswd.send_instructions_button": "Send instructions", | |
41 | + "forgotPasswd.info": "Ao clicar no botão acima, você receberá um email com instruções para criar uma nova senha.", | |
42 | + "forgotPasswd.email_sent.title.info": "Altere sua senha", | |
43 | + "forgotPasswd.email_sent.message.info": "Siga as instruções enviadas por e-mail para alterar sua senha", | |
44 | + "forgotPasswd.not_found.title.error": "Usuário não encontrado", | |
45 | + "forgotPasswd.not_found.message.error": "Não foi possível recuperar a senha deste usuário", | |
39 | 46 | "navbar.content_viewer_actions.new_item": "Novo Item", |
40 | 47 | "navbar.profile_actions.new_item": "Novo Item", |
41 | 48 | "navbar.content_viewer_actions.new_post": "Novo Post", |
... | ... | @@ -43,6 +50,8 @@ |
43 | 50 | "navbar.profile_actions.new_discussion": "Nova Discussão", |
44 | 51 | "notification.error.default.message": "Algo deu errado!", |
45 | 52 | "notification.error.default.title": "Oops...", |
53 | + "notification.info.default.message": "", | |
54 | + "notification.info.default.title": "Informação", | |
46 | 55 | "notification.profile.not_found": "Página não encontrada", |
47 | 56 | "notification.http_error.401.message": "Não autorizado", |
48 | 57 | "notification.http_error.500.message": "Erro no servidor", | ... | ... |