This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit 37703e1869ddacfd3d51e6f99a58da81d8b8a6a0 Author: Yannick Martel <martel@©odelutin.com> Date: Mon Feb 16 17:38:25 2015 +0100 review user error and success on operation : add notifications --- .../services/CoselmarWebServiceSupport.java | 8 ++ .../errors/MailAlreadyExistingException.java | 37 +++++++ .../ifremer/coselmar/services/v1/ErrorAction.java | 9 ++ .../coselmar/services/v1/UsersWebService.java | 15 +-- coselmar-rest/src/main/resources/mapping | 5 +- coselmar-ui/pom.xml | 6 ++ coselmar-ui/src/main/webapp/i18n/en.js | 5 + coselmar-ui/src/main/webapp/i18n/fr.js | 6 ++ coselmar-ui/src/main/webapp/index.html | 8 +- .../src/main/webapp/js/coselmar-controllers.js | 117 ++++++++++++++++++--- .../src/main/webapp/js/coselmar-error-services.js | 62 +++++++++++ coselmar-ui/src/main/webapp/js/coselmar.js | 2 +- .../main/webapp/views/notificationTemplate.html | 16 +++ pom.xml | 8 ++ 14 files changed, 277 insertions(+), 27 deletions(-) diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java index 079560d..5fdb1c1 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java @@ -176,6 +176,14 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl log.error("Error during JWT verification : bad claims!", e); } throw new InvalidCredentialException("Error with claims"); + + } catch (IllegalStateException e) { + // No token set + if (log.isErrorEnabled()) { + log.error("Error during JWT verification : no token!", e); + } + throw new InvalidCredentialException("Seems no user connected"); + } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/MailAlreadyExistingException.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/MailAlreadyExistingException.java new file mode 100644 index 0000000..a18c0d6 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/MailAlreadyExistingException.java @@ -0,0 +1,37 @@ +package fr.ifremer.coselmar.services.errors; + +/* + * #%L + * Coselmar :: Rest Services + * $Id:$ + * $HeadURL:$ + * %% + * Copyright (C) 2014 Ifremer, Code Lutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import java.security.InvalidParameterException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class MailAlreadyExistingException extends InvalidParameterException { + + public MailAlreadyExistingException(String message) { + super(message); + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java index b98e642..24d34e6 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java @@ -71,4 +71,13 @@ public class ErrorAction extends WebMotionController { return render; } + + public Render on409(HttpContext context, Exception e) { + + CoselmarRestUtil.prepareResponse(context); + + Render render = renderError(HttpServletResponse.SC_CONFLICT, e.getMessage()); + return render; + + } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java index afaf5cc..d3fb0d6 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java @@ -53,6 +53,7 @@ import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; +import fr.ifremer.coselmar.services.errors.MailAlreadyExistingException; import fr.ifremer.coselmar.services.errors.UnauthorizedException; import org.apache.commons.io.Charsets; import org.apache.commons.lang3.StringUtils; @@ -199,23 +200,23 @@ public class UsersWebService extends CoselmarWebServiceSupport { return result; } - public void addUser(UserBean user) throws InvalidParameterException, InvalidCredentialException, UnauthorizedException { + public void addUser(UserBean user) throws MailAlreadyExistingException, InvalidCredentialException, UnauthorizedException { Preconditions.checkNotNull(user); // Check authentication String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); - // Who is allowed here ? Admin and user himself + // Who is allowed here ? Admin and Superviseur if (!StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name()) && (StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.SUPERVISOR.name()) && !StringUtils.equals(user.getRole(), CoselmarUserRole.CLIENT.name())) ) { if (log.isDebugEnabled()) { - String message = String.format("A non admin, non supervisor user is trying to access users list"); + String message = String.format("A non admin, non supervisor user is trying to create user"); log.debug(message); } - throw new UnauthorizedException("Not allowed to see users"); + throw new UnauthorizedException("Not allowed to add user"); } CoselmarUser userEntity = getCoselmarUserDao().create(); @@ -258,7 +259,7 @@ public class UsersWebService extends CoselmarWebServiceSupport { } } - public void modifyUser(UserBean user) throws InvalidCredentialException, UnauthorizedException, InvalidParameterException, TopiaNoResultException { + public void modifyUser(UserBean user) throws InvalidCredentialException, UnauthorizedException, InvalidParameterException, TopiaNoResultException, MailAlreadyExistingException { // Check authentication String authorization = getContext().getHeader("Authorization"); @@ -456,7 +457,7 @@ public class UsersWebService extends CoselmarWebServiceSupport { * @param userId : the current user#id : this parameter is needed to exclude from the search this user, cause it could already have this mail * @throws InvalidParameterException if the mail is already used. */ - protected void checkMailUniqueness(String mail, String userId) throws InvalidParameterException { + protected void checkMailUniqueness(String mail, String userId) throws MailAlreadyExistingException { boolean mailAlreadyUsed; if (StringUtils.isNotBlank(userId)) { @@ -468,7 +469,7 @@ public class UsersWebService extends CoselmarWebServiceSupport { if (mailAlreadyUsed) { String msg = String.format("mail '%s' is already used", mail); - throw new InvalidParameterException(msg); + throw new MailAlreadyExistingException(msg); } } diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index 3d166f2..ec66430 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -16,8 +16,9 @@ default.render=fr.ifremer.coselmar.services.CoselmarRender fr.ifremer.coselmar.services.errors.InvalidCredentialException ErrorAction.on401 fr.ifremer.coselmar.services.errors.UnauthorizedException ErrorAction.on403 -fr.ifremer.coselmar.services.errors.NoResultException ErrorAction.on404 -fr.ifremer.coselmar.exceptions.CoselmarTechnicalException ErrorAction.on500 +fr.ifremer.coselmar.services.errors.NoResultException ErrorAction.on404 +fr.ifremer.coselmar.services.errors.MailAlreadyExistingException ErrorAction.on409 +fr.ifremer.coselmar.exceptions.CoselmarTechnicalException ErrorAction.on500 org.nuiton.topia.persistence.TopiaNoResultException ErrorAction.on404 [actions] diff --git a/coselmar-ui/pom.xml b/coselmar-ui/pom.xml index 0bde332..f23f090 100644 --- a/coselmar-ui/pom.xml +++ b/coselmar-ui/pom.xml @@ -87,6 +87,12 @@ <scope>runtime</scope> </dependency> + <dependency> + <groupId>org.webjars</groupId> + <artifactId>angular-notify</artifactId> + <scope>runtime</scope> + </dependency> + </dependencies> <build> diff --git a/coselmar-ui/src/main/webapp/i18n/en.js b/coselmar-ui/src/main/webapp/i18n/en.js index 0721a06..6bcfa7d 100644 --- a/coselmar-ui/src/main/webapp/i18n/en.js +++ b/coselmar-ui/src/main/webapp/i18n/en.js @@ -221,6 +221,11 @@ var translateEN = { "user.message.newPasswordSent" : "A new password has been sent.", "user.message.wannaSimpleLogin" : "Use simple login, not a mail (Warning : no password could be sent)", "user.message.invalidLogin" : "Login is mandatory.", +"user.message.created" : "User well created.", +"user.message.deleted" : "User well deleted.", +"user.message.disable" : "User well disable.", +"user.message.enable" : "User well enable.", +"user.message.mail.alreadyExisting" : "The provided mail (or login) already exists.", "user.button.add" : "Add an user", "user.button.add.client" : "Add a client", diff --git a/coselmar-ui/src/main/webapp/i18n/fr.js b/coselmar-ui/src/main/webapp/i18n/fr.js index 29f4708..61534f1 100644 --- a/coselmar-ui/src/main/webapp/i18n/fr.js +++ b/coselmar-ui/src/main/webapp/i18n/fr.js @@ -221,6 +221,11 @@ var translateFR = { "user.message.newPasswordSent" : "Un nouveau mot de passe a été envoyé.", "user.message.wannaSimpleLogin" : "Utiliser un simple identifiant et non un courriel (Attention : aucun mot de passe ne pourra être envoyé.)", "user.message.invalidLogin" : "L'identifiant est obligatoire.", +"user.message.created" : "Utilisateur créé.", +"user.message.deleted" : "Utilisateur supprimé.", +"user.message.disable" : "Utilisateur désactivé.", +"user.message.enable" : "Utilisateur réactivé.", +"user.message.mail.alreadyExisting" : "Le courriel (ou identifiant) saisi est déjà attribué.", "user.button.add" : "Ajouter un utilisateur", "user.button.add.client" : "Ajouter un client", @@ -295,4 +300,5 @@ var translateFR = { <li>faire une recherche globale sur le référentiel document/question et d'extraire le résultat sous un format exploitable</li>\ </p>", "home.connected.lastProjects" : "Derniers projets", + } \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/index.html b/coselmar-ui/src/main/webapp/index.html index 9c20739..53ef995 100644 --- a/coselmar-ui/src/main/webapp/index.html +++ b/coselmar-ui/src/main/webapp/index.html @@ -26,7 +26,8 @@ <head> <meta charset="utf-8" /> <title>Coselmar {{ 'application.title' | translate}}</title> - + + <link rel="stylesheet" href="webjars/angular-notify/2.0.2/angular-notify.min.css"> <link rel="stylesheet" href="webjars/bootstrap/3.3.2/css/bootstrap.css"> <link rel="stylesheet" href="webjars/font-awesome/4.3.0/css/font-awesome.css"> <link rel="stylesheet" href="css/coselmar.css"> @@ -42,7 +43,9 @@ <script src="webjars/angular-translate/2.6.0/angular-translate.js"></script> <script src="webjars/angular-dynamic-locale/0.1.27/src/tmhDynamicLocale.js"></script> - <!--TODO ymartel 20141203 : extract version, or use wro --> + <script src="webjars/angular-notify/2.0.2/angular-notify.min.js"></script> + + <script src="webjars/angular-ui-select/0.9.6/select.js"></script> <link rel="stylesheet" href="webjars/angular-ui-select/0.9.6/select.css"> <script src="webjars/angular-ui-bootstrap/0.12.0/ui-bootstrap.js"></script> @@ -58,6 +61,7 @@ <script src="js/coselmar-user-services.js"></script> <script src="js/coselmar-questions-services.js"></script> <script src="js/coselmar-admin-services.js"></script> + <script src="js/coselmar-error-services.js"></script> </head> <body> diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 76057ac..c76d677 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -435,7 +435,7 @@ coselmarControllers.directive('ngFileModel', ['$parse', function ($parse) { ///////////////////////////////////////////////// // Controller for All User View -coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', '$location', 'userService', function($scope, $route, $routeParams, $location, userService){ +coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', '$location', 'userService', 'notify', function($scope, $route, $routeParams, $location, userService, notify){ //manage keywords if given $scope.search = { searchKeywords : []}; @@ -461,6 +461,12 @@ coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', $scope.deleteUser = function(userId){ userService.deleteUser(userId, $scope, function() { + // Notification + notify({ + message: "user.message.delete", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); // Go back to documents list $route.reload(); }); @@ -470,6 +476,12 @@ coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', user.active = false; userService.saveUser(user, function() { + // Notification + notify({ + message: "user.message.disable", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); // Go back to users list $location.path("/users"); }); @@ -480,6 +492,12 @@ coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', user.active = true; userService.saveUser(user, function() { + // Notification + notify({ + message: "user.message.enable", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); // Go back to users list $location.path("/users"); }); @@ -497,7 +515,6 @@ coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', // Because "ALL" role is just a hack to select no role, remove it from example delete $scope.example.role; }; - console.log($scope.example); userService.getAdvancedUsers($scope.example, function(users){ $scope.users = users; @@ -511,9 +528,12 @@ coselmarControllers.controller("UsersCtrl", ['$scope', '$route', '$routeParams', // Controller for new user View -coselmarControllers.controller("NewUserCtrl", ['$scope', '$route', '$location', 'userService', function($scope, $route, $location, userService){ +coselmarControllers.controller("NewUserCtrl", ['$scope', '$route', '$location', 'userService', 'notify', 'errorService', + function($scope, $route, $location, userService, notify, errorService) { - if ($scope.currentUser.role == 'ADMIN') { + if (!$scope.currentUser) { + errorService.reject401(); + } else if ($scope.currentUser.role == 'ADMIN') { $scope.user = {'role' : 'EXPERT'}; } else if ($scope.currentUser.role == 'SUPERVISOR') { $scope.user = {'role' : 'CLIENT'}; @@ -526,11 +546,35 @@ coselmarControllers.controller("NewUserCtrl", ['$scope', '$route', '$location', // Call service to create a new user if(isValidForm) { userService.saveUser($scope.user, function() { + notify({ + message: "user.message.created", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); $location.path("/users"); - },function(error) { - //TODO ymartel 20141118 : deal with error.status or statusText - console.log("error occurs"); - console.log(error.s); + + }, function(error) { + if (error.status == 409) { + // Mail already existing + notify({ + message: "user.message.mail.alreadyExisting", + classes: "alert-danger", + templateUrl: "views/notificationTemplate.html" + }); + } else if (error.status == 403) { + errorService.reject403(); + + } else if (error.status == 401) { + errorService.reject401(); + + } else { + notify({ + message: "common.message.internalError", + classes: "alert-danger", + templateUrl: "views/notificationTemplate.html" + }); + + } }); } @@ -539,7 +583,8 @@ coselmarControllers.controller("NewUserCtrl", ['$scope', '$route', '$location', // Controller for single User View & Edit coselmarControllers.controller("UserViewCtrl", - ['$scope', '$route', '$location', 'userService', '$routeParams', function($scope, $route, $location, userService, $routeParams) { + ['$scope', '$route', '$location', 'userService', '$routeParams', 'notify', 'errorService', + function($scope, $route, $location, userService, $routeParams, notify, errorService) { $scope.editMode = $routeParams.edit; if ($scope.editMode) { @@ -552,9 +597,16 @@ coselmarControllers.controller("UserViewCtrl", $scope.deleteUser = function(userId){ userService.deleteUser(userId, $scope, function() { + // Notification + notify({ + message: "user.message.deleted", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); // Go back to users list $location.path("/users"); - }); + + }, errorService.defaultFailOnCall(error)); }; @@ -563,9 +615,16 @@ coselmarControllers.controller("UserViewCtrl", var user = $scope.user; user.active = false; userService.saveUser(user, function() { + // Notification + notify({ + message: "user.message.disable", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); // Go back to users list $route.reload(); - }); + + }, errorService.defaultFailOnCall(error)); }; @@ -574,9 +633,16 @@ coselmarControllers.controller("UserViewCtrl", var user = $scope.user; user.active = true; userService.saveUser(user, function() { + // Notification + notify({ + message: "user.message.enable", + classes: "alert-success", + templateUrl: "views/notificationTemplate.html" + }); // Go back to users list $route.reload(); - }); + + }, errorService.defaultFailOnCall(error)); }; @@ -598,9 +664,30 @@ coselmarControllers.controller("UserViewCtrl", } },function(error) { - //TODO ymartel 20141118 : deal with error.status or statusText - console.log("error occurs"); - console.log(error.s); + if (error.status == 409) { + // Mail already existing + notify({ + message: "user.message.mail.alreadyExisting", + classes: "alert-danger", + templateUrl: "views/notificationTemplate.html" + }); + } else if (error.status = 403) { + errorService.reject403(); + + } else if (error.status = 401) { + errorService.reject401(); + + } else if (error.status = 404) { + errorService.reject404(); + + } else { + notify({ + message: "common.message.internalError", + classes: "alert-danger", + templateUrl: "views/notificationTemplate.html" + }); + + } }); } }; diff --git a/coselmar-ui/src/main/webapp/js/coselmar-error-services.js b/coselmar-ui/src/main/webapp/js/coselmar-error-services.js new file mode 100644 index 0000000..c86de65 --- /dev/null +++ b/coselmar-ui/src/main/webapp/js/coselmar-error-services.js @@ -0,0 +1,62 @@ +/* + * #%L + * Coselmar :: UI + * $Id:$ + * $HeadURL:$ + * %% + * Copyright (C) 2014 Ifremer, Code Lutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +coselmarServices.factory('errorService', ['$location', 'notify', function($location, notify){ + return { + reject401 : function() { + // create return to path wanted + var returnTo = $location.path(); + $location.path("/401").search('returnTo', returnTo); + }, + reject403 : function() { + // create return to path wanted + $location.path("/403"); + }, + reject404 : function() { + // create return to path wanted + $location.path("/404"); + }, + + // Default function for error during call processing + defaultFailOnCall : function(error) { + + if (error.status == 403) { + errorService.reject403(); + + } else if (error.status == 401) { + errorService.reject401(); + + } else if (error.status == 404) { + errorService.reject404(); + + } else { + notify({ + message: "common.message.internalError", + classes: "alert-danger", + templateUrl: "views/notificationTemplate.html" + }); + + } + } + }; +}]); \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/js/coselmar.js b/coselmar-ui/src/main/webapp/js/coselmar.js index 988f22e..d5b7b22 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar.js +++ b/coselmar-ui/src/main/webapp/js/coselmar.js @@ -22,7 +22,7 @@ * #L% */ var coselmarApp = angular.module("coselmarApp", ['ngRoute', 'ngResource', 'ngMessages', 'ngAnimate', - 'pascalprecht.translate', 'tmh.dynamicLocale', + 'pascalprecht.translate', 'tmh.dynamicLocale', 'cgNotify', 'angular-jwt', 'coselmarControllers', 'coselmarServices']); coselmarApp.config(['$routeProvider', function($routeProvider) { diff --git a/coselmar-ui/src/main/webapp/views/notificationTemplate.html b/coselmar-ui/src/main/webapp/views/notificationTemplate.html new file mode 100644 index 0000000..67722cc --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/notificationTemplate.html @@ -0,0 +1,16 @@ +<div class="cg-notify-message" ng-class="$classes"> + + <div ng-show="!$messageTemplate"> + {{$message | translate}} + </div> + + <div ng-show="$messageTemplate" class="cg-notify-message-template"> + + </div> + + <button type="button" class="cg-notify-close" ng-click="$close()"> + <span aria-hidden="true">×</span> + <span class="cg-notify-sr-only">Close</span> + </button> + +</div> \ No newline at end of file diff --git a/pom.xml b/pom.xml index f1ce828..84ca883 100644 --- a/pom.xml +++ b/pom.xml @@ -148,6 +148,7 @@ <angularUiBootstrapVersion>0.12.0</angularUiBootstrapVersion> <angularTranslateVersion>2.6.0</angularTranslateVersion> <angularDynamicLocaleVersion>0.1.27</angularDynamicLocaleVersion> + <angularNotifyVersion>2.0.2</angularNotifyVersion> <jqueryVersion>2.1.3</jqueryVersion> <fontAwesomeVersion>4.3.0-1</fontAwesomeVersion> @@ -428,6 +429,13 @@ <dependency> <groupId>org.webjars</groupId> + <artifactId>angular-notify</artifactId> + <version>${angularNotifyVersion}</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>${jqueryVersion}</version> <scope>runtime</scope> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.