From c5d7cc3f72b87b4e41adeae9c0ccef193e2f20b2 Mon Sep 17 00:00:00 2001 From: Mathieu Dulac Date: Wed, 25 Nov 2020 18:09:17 +0100 Subject: [PATCH] Edition du profil utilisateur (#858) Edition du profil utilisateur (#858) --- app/controllers/UserController.scala | 75 +++++++++++++- app/helper/StringHelper.scala | 5 + app/models/EventType.scala | 5 + app/models/formModels.scala | 28 ++++- app/services/UserService.scala | 29 ++++-- app/views/editProfile.scala.html | 80 +++++++++++++++ app/views/helpers/menuNav.scala.html | 1 - app/views/main.scala.html | 13 ++- conf/routes | 2 + public/stylesheets/main.css | 148 +++++++++++++++++---------- 10 files changed, 318 insertions(+), 68 deletions(-) create mode 100644 app/views/editProfile.scala.html diff --git a/app/controllers/UserController.scala b/app/controllers/UserController.scala index e4888c884..1d3e32ff3 100644 --- a/app/controllers/UserController.scala +++ b/app/controllers/UserController.scala @@ -34,13 +34,17 @@ import models.EventType.{ UserEdited, UserIsUsed, UserNotFound, + UserProfileShowed, + UserProfileShowedError, + UserProfileUpdated, + UserProfileUpdatedError, UserShowed, UsersCreated, UsersShowed, ViewUserUnauthorized } import models._ -import models.formModels.ValidateSubscriptionForm +import models.formModels.{EditProfileFormData, ValidateSubscriptionForm} import org.postgresql.util.PSQLException import org.webjars.play.WebJarsUtil import play.api.Configuration @@ -53,6 +57,7 @@ import play.filters.csrf.CSRF.Token import serializers.{Keys, UserAndGroupCsvSerializer} import services._ +import scala.concurrent.Future.successful import scala.concurrent.{ExecutionContext, Future} import scala.util.Try @@ -71,9 +76,73 @@ case class UserController @Inject() ( with UserOperators with GroupOperators { + def showEditProfile = + loginAction.async { implicit request => + // Should be better if User could contains List[UserGroup] instead of List[UUID] + val user = request.currentUser + val profile = EditProfileFormData( + user.firstName.orEmpty, + user.lastName.orEmpty, + user.qualite, + user.phoneNumber.orEmpty + ) + val form = EditProfileFormData.form.fill(profile) + groupService + .byIdsFuture(user.groupIds) + .map { groups => + eventService.log(UserProfileShowed, "Visualise la modification de profil") + Ok(views.html.editProfile(request.currentUser, request.rights)(form, user.email, groups)) + } + .recoverWith { case exception => + val message = + s"Impossible de visualiser la modification de profil : ${exception.getMessage}" + eventService.log(UserProfileShowedError, message) + Future.successful(InternalServerError(views.html.welcome(user, request.rights))) + } + } + + def editProfile = + loginAction.async { implicit request => + val user = request.currentUser + if (user.sharedAccount) { + eventService.log(UserProfileUpdatedError, "Impossible de modifier un profil partagé") + successful(BadRequest(views.html.welcome(user, request.rights))) + } else + EditProfileFormData.form + .bindFromRequest() + .fold( + errors => { + eventService.log(UserProfileUpdatedError, "Erreur lors de la modification du profil") + groupService + .byIdsFuture(user.groupIds) + .map(groups => + BadRequest( + views.html.editProfile(user, request.rights)(errors, user.email, groups) + ) + ) + }, + success => { + import success._ + val edited = + userService.editProfile(user.id)(firstName, lastName, qualite, phoneNumber) + successful(edited) + .map { _ => + val message = "Votre profil a bien été modifié" + eventService.log(UserProfileUpdated, message) + Redirect(routes.UserController.editProfile()).flashing("success" -> message) + } + .recover { e => + val errorMessage = s"Erreur lors de la modification du profil: ${e.getMessage}" + eventService.log(UserProfileUpdatedError, errorMessage) + InternalServerError(views.html.welcome(user, request.rights)) + } + } + ) + } + def home = loginAction { - TemporaryRedirect(controllers.routes.UserController.all(Area.allArea.id).url) + TemporaryRedirect(routes.UserController.all(Area.allArea.id).url) } def all(areaId: UUID): Action[AnyContent] = @@ -282,7 +351,7 @@ case class UserController @Inject() ( val message = s"Utilisateur $userId / ${user.email} a été supprimé" eventService.log(UserDeleted, message, involvesUser = Some(user)) Future( - Redirect(controllers.routes.UserController.home()).flashing("success" -> message) + Redirect(routes.UserController.home()).flashing("success" -> message) ) } } diff --git a/app/helper/StringHelper.scala b/app/helper/StringHelper.scala index 2319faf02..dd3ca1085 100644 --- a/app/helper/StringHelper.scala +++ b/app/helper/StringHelper.scala @@ -57,6 +57,11 @@ object StringHelper { def unapply(s: String): Option[String] = s.some.map(_.trim).filter(_.nonEmpty) } + implicit class StringOps(s: String) { + def normalized = StringHelper.commonStringInputNormalization(s) + def capitalizeWords = StringHelper.capitalizeName(s) + } + implicit class StringListOps(list: List[String]) { def mkStringIfNonEmpty(start: String, sep: String, end: String) = { diff --git a/app/models/EventType.scala b/app/models/EventType.scala index 0857f2a44..d297a6ff6 100644 --- a/app/models/EventType.scala +++ b/app/models/EventType.scala @@ -145,4 +145,9 @@ object EventType { object StatsIncorrectSetup extends Warn object TryLoginByKey extends Info object UnknownEmail extends Warn + + object UserProfileShowed extends Info + object UserProfileShowedError extends Error + object UserProfileUpdated extends Info + object UserProfileUpdatedError extends Error } diff --git a/app/models/formModels.scala b/app/models/formModels.scala index 91564f52d..e056b2ad1 100644 --- a/app/models/formModels.scala +++ b/app/models/formModels.scala @@ -4,11 +4,37 @@ import java.util.UUID import play.api.data.Form import play.api.data.Forms._ -import play.api.data.validation.Constraints.maxLength +import play.api.data.validation.Constraints.{maxLength, nonEmpty, pattern} import play.api.data.validation.{Constraint, Invalid, Valid, ValidationError} object formModels { + final case class EditProfileFormData( + firstName: String, + lastName: String, + qualite: String, + phoneNumber: String + ) + + object EditProfileFormData { + + val form: Form[EditProfileFormData] = + Form( + mapping( + "firstName" -> text.verifying(maxLength(100), nonEmpty), + "lastName" -> text.verifying(maxLength(100), nonEmpty), + "qualite" -> text.verifying(maxLength(100), nonEmpty), + "phone-number" -> text.verifying( + pattern( + """0\d \d{2} \d{2} \d{2} \d{2}""".r, + error = "Le format doit être XX XX XX XX XX" + ) + ) + )(EditProfileFormData.apply)(EditProfileFormData.unapply) + ) + + } + case class ApplicationFormData( subject: String, description: String, diff --git a/app/services/UserService.scala b/app/services/UserService.scala index ffe933da3..ee420738d 100644 --- a/app/services/UserService.scala +++ b/app/services/UserService.scala @@ -4,17 +4,12 @@ import java.util.UUID import anorm._ import cats.syntax.all._ -import cats.implicits.{catsKernelStdMonoidForString, catsSyntaxOption} +import helper.StringHelper.{capitalizeName, normalizeNFKC, StringOps} import helper.{Hash, Time} import javax.inject.Inject import models.User -import javax.inject.Inject -import models.User -import models.formModels.ValidateSubscriptionForm import org.postgresql.util.PSQLException import play.api.db.Database -import play.api.db.Database -import views.html.helper.form import scala.concurrent.Future @@ -251,4 +246,26 @@ class UserService @Inject() ( """.executeUpdate() } + def editProfile(userId: UUID)( + firstName: String, + lastName: String, + qualite: String, + phoneNumber: String + ) = + db.withConnection { implicit cnx => + val normalizedFirstName = firstName.normalized + val normalizedLastName = lastName.normalized + val normalizedQualite = qualite.normalized + val name = s"${normalizedLastName.toUpperCase} ${normalizedFirstName.capitalizeWords}" + SQL""" + UPDATE "user" SET + name = $name, + first_name = ${normalizedFirstName.capitalizeWords}, + last_name = ${normalizedLastName.capitalizeWords}, + qualite = $normalizedQualite, + phone_number = $phoneNumber + WHERE id = $userId::uuid + """.executeUpdate() + } + } diff --git a/app/views/editProfile.scala.html b/app/views/editProfile.scala.html new file mode 100644 index 000000000..cb8f1fceb --- /dev/null +++ b/app/views/editProfile.scala.html @@ -0,0 +1,80 @@ +@import _root_.helper.MDLForms._ +@import models._ +@import models.formModels.EditProfileFormData +@(currentUser: User, currentUserRights: Authorization.UserRights)(form: Form[EditProfileFormData], email: String, groups: List[UserGroup])(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, messagesProvider: MessagesProvider, request: RequestHeader) + + @main(currentUser, currentUserRights)(s"Mon profil") { + + } { + @helper.form(action = routes.UserController.editProfile(), "method" -> "post", "class" -> "mdl-grid mdl-cell mdl-cell--12-col") { +

Mon profil

+
+ Vous pouvez modifier les informations vous concernant
+
+ @helper.CSRF.formField + + @if(form.hasGlobalErrors) { +
@form.globalErrors.mkString(", ")
+ } + @helper.input(form("id"), "label" -> "Id", "class" -> "hidden") { (id, name, value, args) => + + }
+ @helper.input(form("email"), "label" -> "Email") { (id, name, _, args) => + + }
+ @helper.input(form("lastName"), "label" -> "Nom") { (id, name, value, args) => + + }
+ @helper.input(form("firstName"), "label" -> "Prénom") { (id, name, value, args) => + + }
+ @helper.input(form("qualite"), "label" -> "Fonction / Rôle") { (id, name, value, args) => + + }
+ @helper.input(form("phone-number"), "label" -> "Numéro de téléphone") { (id, name, value, args) => + + }
+ @if(groups.isEmpty) { +
Vous n’appartenez à aucun groupe
+ } + @if(groups.nonEmpty) { +
Vous appartenez aux groupes suivants :
+ @for(group <- groups) { +
@group.name
+ } + } +
+
+ +
+ } + } { + } diff --git a/app/views/helpers/menuNav.scala.html b/app/views/helpers/menuNav.scala.html index 4f7a48d9f..159c450c9 100644 --- a/app/views/helpers/menuNav.scala.html +++ b/app/views/helpers/menuNav.scala.html @@ -15,7 +15,6 @@ folder_openMes demandes - @if(Authorization.isAdmin(currentUserRights)) { placeTerritoires diff --git a/app/views/main.scala.html b/app/views/main.scala.html index 3220ddb95..95116eeef 100644 --- a/app/views/main.scala.html +++ b/app/views/main.scala.html @@ -62,11 +62,20 @@