Skip to content

Commit

Permalink
Many improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Portals committed Jun 14, 2024
1 parent f20fc73 commit f144a9a
Show file tree
Hide file tree
Showing 29 changed files with 220 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package it.chalmers.gamma.adapter.primary.web;

import it.chalmers.gamma.adapter.secondary.image.ImageFile;
import it.chalmers.gamma.app.common.Email.EmailValidator;
import it.chalmers.gamma.app.image.domain.ImageService;
import it.chalmers.gamma.app.user.MeFacade;
import it.chalmers.gamma.app.user.domain.Nick.NickValidator;
import it.chalmers.gamma.app.user.domain.UnencryptedPassword.UnencryptedPasswordValidator;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import static it.chalmers.gamma.adapter.primary.web.WebValidationHelper.validateObject;
import static it.chalmers.gamma.app.user.domain.FirstName.FirstNameValidator;
import static it.chalmers.gamma.app.user.domain.LastName.LastNameValidator;

@Controller
public class HomeController {

Expand All @@ -29,10 +35,10 @@ public ModelAndView getMe(
ModelAndView mv = new ModelAndView();

if (htmxRequest) {
mv.setViewName("pages/me");
mv.setViewName("home/page");
} else {
mv.setViewName("index");
mv.addObject("page", "pages/me");
mv.addObject("page", "home/page");
}

MeFacade.MeDTO me = this.meFacade.getMe();
Expand All @@ -44,21 +50,28 @@ public ModelAndView getMe(
}

public record EditMe(
String nick, String firstName, String lastName, String email, String language) {}
@ValidatedWith(NickValidator.class) String nick,
@ValidatedWith(FirstNameValidator.class) String firstName,
@ValidatedWith(LastNameValidator.class) String lastName,
@ValidatedWith(EmailValidator.class) String email,
String language) {}

private ModelAndView createEditMeMV(EditMe form) {
ModelAndView mv = new ModelAndView();
mv.setViewName("home/edit-me");
mv.addObject("form", form);

return mv;
}

@GetMapping("/me/edit")
public ModelAndView getEditMe(
@RequestHeader(value = "HX-Request", required = true) boolean htmxRequest) {
MeFacade.MeDTO me = this.meFacade.getMe();

ModelAndView mv = new ModelAndView();

EditMe form = new EditMe(me.nick(), me.firstName(), me.lastName(), me.email(), me.language());

mv.setViewName("partial/edit-me");
mv.addObject("form", form);

return mv;
return createEditMeMV(form);
}

@PutMapping("/me")
Expand All @@ -68,23 +81,22 @@ public ModelAndView editMe(
final BindingResult bindingResult) {
ModelAndView mv = new ModelAndView();

try {
this.meFacade.updateMe(
new MeFacade.UpdateMe(
form.nick, form.firstName, form.lastName, form.email, form.language));
} catch (IllegalArgumentException e) {
bindingResult.addError(new ObjectError("global", e.getMessage()));
validateObject(form, bindingResult);

mv.setViewName("partial/edit-me");
if (bindingResult.hasErrors()) {
mv.setViewName("home/edit-me");
mv.addObject("form", form);
mv.addObject(BindingResult.MODEL_KEY_PREFIX + "form", bindingResult);

return mv;
}

this.meFacade.updateMe(
new MeFacade.UpdateMe(form.nick, form.firstName, form.lastName, form.email, form.language));

MeFacade.MeDTO me = this.meFacade.getMe();

mv.setViewName("partial/edited-me");
mv.setViewName("home/edited-me");
mv.addObject(
"me",
new MeFacade.MeDTO(
Expand All @@ -109,21 +121,23 @@ public ModelAndView getCancelEdit(

MeFacade.MeDTO me = this.meFacade.getMe();

mv.setViewName("pages/me :: userinfo");
mv.setViewName("home/page :: userinfo");
mv.addObject("me", me);

return mv;
}

public record EditPasswordForm(
String currentPassword, String newPassword, String confirmNewPassword) {}
String currentPassword,
@ValidatedWith(UnencryptedPasswordValidator.class) String newPassword,
@ValidatedWith(UnencryptedPasswordValidator.class) String confirmNewPassword) {}

@GetMapping("/me/edit-password")
public ModelAndView getEditPassword(
@RequestHeader(value = "HX-Request", required = true) boolean htmxRequest) {
ModelAndView mv = new ModelAndView();

mv.setViewName("partial/edit-me-password");
mv.setViewName("home/edit-me-password");
mv.addObject("form", new EditPasswordForm("", "", ""));

return mv;
Expand All @@ -134,49 +148,53 @@ public ModelAndView editPassword(
@RequestHeader(value = "HX-Request", required = true) boolean htmxRequest,
EditPasswordForm form,
final BindingResult bindingResult) {
ModelAndView mv = new ModelAndView();

validateObject(form, bindingResult);

try {
this.meFacade.updatePassword(
new MeFacade.UpdatePassword(
form.currentPassword, form.newPassword, form.confirmNewPassword));
} catch (MeFacade.NewPasswordNotConfirmedException e) {
if (!bindingResult.hasErrors()) {
this.meFacade.updatePassword(
new MeFacade.UpdatePassword(
form.currentPassword, form.newPassword, form.confirmNewPassword));
}
} catch (IllegalArgumentException | MeFacade.NewPasswordNotConfirmedException e) {
bindingResult.addError(
new FieldError("form", "confirmNewPassword", "Passwords were not the same"));
} catch (MeFacade.PasswordIncorrectException e) {
bindingResult.addError(new FieldError("form", "currentPassword", "Passwords not correct"));
}

ModelAndView mv = new ModelAndView();

mv.setViewName("partial/edit-me-password");
mv.addObject("form", new MeFacade.UpdatePassword("", "", ""));
if (bindingResult.hasErrors()) {
mv.setViewName("home/edit-me-password");
mv.addObject("form", form);
mv.addObject(BindingResult.MODEL_KEY_PREFIX + "form", bindingResult);

return mv;
}

MeFacade.MeDTO me = this.meFacade.getMe();

ModelAndView mv = new ModelAndView();

mv.setViewName("partial/edited-me-password");
mv.setViewName("home/edited-me-password");
mv.addObject("me", me);

return mv;
}

@PutMapping("/me/avatar")
public ModelAndView editAvatar(@RequestParam("file") MultipartFile file) {
ModelAndView mv = new ModelAndView();
try {
this.meFacade.setAvatar(new ImageFile(file));
} catch (ImageService.ImageCouldNotBeSavedException e) {
throw new RuntimeException(e);
}

MeFacade.MeDTO me = this.meFacade.getMe();

ModelAndView mv = new ModelAndView();
MeFacade.MeDTO me = this.meFacade.getMe();

mv.setViewName("partial/edited-me-avatar");
mv.addObject("random", Math.random());
mv.addObject("meId", me.id());
mv.setViewName("home/edited-me-avatar");
mv.addObject("random", Math.random());
mv.addObject("meId", me.id());
} catch (ImageService.ImageCouldNotBeSavedException e) {
mv.setViewName("home/failed-to-edit-me-avatar");
}

return mv;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import it.chalmers.gamma.security.authentication.AuthenticationExtractor;
import it.chalmers.gamma.security.authentication.UserAuthentication;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
Expand Down Expand Up @@ -36,4 +38,12 @@ public ModelAndView handleAccessDeniedException(HttpServletRequest request) {

return mv;
}

@ExceptionHandler(MaxUploadSizeExceededException.class)
public ModelAndView handleMaxSizeException(HttpServletResponse response) {
response.addHeader("HX-Retarget", "#alerts");
response.addHeader("HX-Reswap", "afterbegin");

return new ModelAndView("common/content-too-large");
}
}
33 changes: 19 additions & 14 deletions app/src/main/java/it/chalmers/gamma/app/common/Email.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
package it.chalmers.gamma.app.common;

import static it.chalmers.gamma.app.validation.ValidationHelper.*;

import it.chalmers.gamma.app.user.domain.UserIdentifier;
import it.chalmers.gamma.app.validation.ValidationResult;
import it.chalmers.gamma.app.validation.Validator;
import java.io.Serializable;
import java.util.Objects;
import java.util.regex.Pattern;
import org.springframework.web.util.HtmlUtils;

public record Email(String value) implements UserIdentifier, Serializable {

private static final Pattern emailPattern =
Pattern.compile(
"^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$");

public Email {
Objects.requireNonNull(value);
throwIfFailed(new EmailValidator().validate(value));
}

value = HtmlUtils.htmlEscape(value, "UTF-8");
public static class EmailValidator implements Validator<String> {

if (!emailPattern.matcher(value).matches()) {
throw new IllegalArgumentException("Email does not look valid");
}
}
private static final Pattern emailPattern =
Pattern.compile(
"^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$");

private static final Validator<String> emailRegex =
value ->
result(
EmailValidator.emailPattern.matcher(value).matches(), "Email does not look valid");

public static boolean isValidEmail(String possibleEmail) {
return emailPattern.matcher(possibleEmail).matches();
@Override
public ValidationResult validate(String value) {
return withValidators(IS_NOT_EMPTY, SANITIZED_HTML, emailRegex).validate(value);
}
}
}
6 changes: 5 additions & 1 deletion app/src/main/java/it/chalmers/gamma/app/user/MeFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public void updateMe(UpdateMe updateMe) {
}

public void updatePassword(UpdatePassword updatePassword)
throws NewPasswordNotConfirmedException {
throws NewPasswordNotConfirmedException, PasswordIncorrectException {
GammaAuthentication authenticated = AuthenticationExtractor.getAuthentication();
if (authenticated instanceof UserAuthentication userAuthentication) {
if (!updatePassword.newPassword.equals(updatePassword.confirmNewPassword)) {
Expand All @@ -122,6 +122,8 @@ public void updatePassword(UpdatePassword updatePassword)
me.id(), new UnencryptedPassword(updatePassword.oldPassword))) {
this.userRepository.setPassword(
me.id(), new UnencryptedPassword(updatePassword.newPassword));
} else {
throw new PasswordIncorrectException();
}
}
}
Expand Down Expand Up @@ -223,4 +225,6 @@ public record UpdateMe(
public record UpdatePassword(String oldPassword, String newPassword, String confirmNewPassword) {}

public static final class NewPasswordNotConfirmedException extends Exception {}

public static final class PasswordIncorrectException extends Exception {}
}
16 changes: 10 additions & 6 deletions app/src/main/java/it/chalmers/gamma/app/user/domain/FirstName.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package it.chalmers.gamma.app.user.domain;

import static it.chalmers.gamma.app.validation.ValidationHelper.*;

import it.chalmers.gamma.app.validation.ValidationResult;
import it.chalmers.gamma.app.validation.Validator;
import java.io.Serializable;
import java.util.Objects;
import org.springframework.web.util.HtmlUtils;

public record FirstName(String value) implements Serializable {

public FirstName {
Objects.requireNonNull(value);
throwIfFailed(new FirstNameValidator().validate(value));
}

value = HtmlUtils.htmlEscape(value, "UTF-8");
public static final class FirstNameValidator implements Validator<String> {

if (value.isEmpty() || value.length() > 50) {
throw new IllegalArgumentException("First name length must be between 1 and 50");
@Override
public ValidationResult validate(String value) {
return withValidators(IS_NOT_EMPTY, SANITIZED_HTML, MAX_LENGTH.apply(50)).validate(value);
}
}
}
16 changes: 10 additions & 6 deletions app/src/main/java/it/chalmers/gamma/app/user/domain/LastName.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package it.chalmers.gamma.app.user.domain;

import static it.chalmers.gamma.app.validation.ValidationHelper.*;

import it.chalmers.gamma.app.validation.ValidationResult;
import it.chalmers.gamma.app.validation.Validator;
import java.io.Serializable;
import java.util.Objects;
import org.springframework.web.util.HtmlUtils;

public record LastName(String value) implements Serializable {

public LastName {
Objects.requireNonNull(value);
throwIfFailed(new LastNameValidator().validate(value));
}

value = HtmlUtils.htmlEscape(value, "UTF-8");
public static final class LastNameValidator implements Validator<String> {

if (value.isEmpty() || value.length() > 50) {
throw new IllegalArgumentException("Last name length must be between 1 and 50");
@Override
public ValidationResult validate(String value) {
return withValidators(IS_NOT_EMPTY, SANITIZED_HTML, MAX_LENGTH.apply(50)).validate(value);
}
}
}
15 changes: 9 additions & 6 deletions app/src/main/java/it/chalmers/gamma/app/user/domain/Nick.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package it.chalmers.gamma.app.user.domain;

import static it.chalmers.gamma.app.validation.ValidationHelper.*;

import it.chalmers.gamma.app.validation.*;
import java.io.Serializable;
import java.util.Objects;
import org.springframework.web.util.HtmlUtils;

public record Nick(String value) implements Serializable {

public Nick {
Objects.requireNonNull(value);
throwIfFailed(new NickValidator().validate(value));
}

value = HtmlUtils.htmlEscape(value, "UTF-8");
public static final class NickValidator implements Validator<String> {

if (value.isEmpty() || value.length() > 30) {
throw new IllegalArgumentException("Nick length must be between 1 and 30");
@Override
public ValidationResult validate(String value) {
return withValidators(IS_NOT_EMPTY, SANITIZED_HTML, MAX_LENGTH.apply(50)).validate(value);
}
}
}
Loading

0 comments on commit f144a9a

Please sign in to comment.