Commit 33647b984dd508f4f2ec493305971379222e0de3

Authored by Daniela Feitosa
1 parent afb4744a
Exists in forgot_password

Ticket #107 Implementation of 'Forgot password'

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 }
... ...
src/app/login/forgot-password.html 0 → 100644
... ... @@ -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
... ... @@ -10,6 +10,12 @@
10 10 margin-top: 20px;
11 11 text-transform: uppercase;
12 12 }
  13 + .form-inline {
  14 + display: inline;
  15 + div {
  16 + display: inline-block;
  17 + }
  18 + }
13 19 }
14 20 }
15 21  
... ...
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",
... ...