Skip to content

Commit 7f3f1d2

Browse files
committed
fix(users): solve problems with update partials
1 parent 9bd2477 commit 7f3f1d2

File tree

13 files changed

+131
-64
lines changed

13 files changed

+131
-64
lines changed

src/main/java/config/SecurityWebApplicationInitializer.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,4 @@
1111
*
1212
* @author sergio
1313
*/
14-
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
15-
16-
}
14+
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* To change this license header, choose License Headers in Project Properties.
3+
* To change this template file, choose Tools | Templates
4+
* and open the template in the editor.
5+
*/
6+
package controllers.admin;
7+
8+
import exceptions.UserAlredyExistsException;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Locale;
12+
import models.User;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
import org.springframework.beans.factory.annotation.Autowired;
16+
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
17+
import org.springframework.stereotype.Controller;
18+
import org.springframework.ui.Model;
19+
import org.springframework.validation.BindingResult;
20+
import org.springframework.validation.annotation.Validated;
21+
import org.springframework.web.bind.WebDataBinder;
22+
import org.springframework.web.bind.annotation.InitBinder;
23+
import org.springframework.web.bind.annotation.ModelAttribute;
24+
import org.springframework.web.bind.annotation.RequestMapping;
25+
import org.springframework.web.bind.annotation.RequestMethod;
26+
import org.springframework.web.bind.annotation.SessionAttributes;
27+
import org.springframework.web.bind.support.SessionStatus;
28+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
29+
import services.UserService;
30+
import services.security.CurrentUser;
31+
32+
/**
33+
*
34+
* @author sergio
35+
*/
36+
@Controller
37+
@RequestMapping("/admin/users/self")
38+
@SessionAttributes({SelfUserController.ATTRIBUTE_NAME})
39+
public class SelfUserController {
40+
41+
private static Logger logger = LoggerFactory.getLogger(SelfUserController.class);
42+
43+
public static final String ATTRIBUTE_NAME = "user";
44+
public static final String BINDING_RESULT_NAME = "org.springframework.validation.BindingResult." + ATTRIBUTE_NAME;
45+
46+
@Autowired
47+
private UserService userService;
48+
@Autowired
49+
private ReloadableResourceBundleMessageSource messageSource;
50+
51+
52+
@InitBinder
53+
void allowFields(WebDataBinder webDataBinder){
54+
webDataBinder.setAllowedFields("username", "email", "fullName");
55+
}
56+
57+
@RequestMapping(method = RequestMethod.GET)
58+
public String self(@CurrentUser User user, Model model) {
59+
60+
/* if "fresh" GET (ie, not redirect w validation errors): */
61+
if(!model.containsAttribute(BINDING_RESULT_NAME)) {
62+
model.addAttribute(ATTRIBUTE_NAME, user);
63+
}
64+
return "admin/user/self";
65+
}
66+
67+
@RequestMapping(method = RequestMethod.POST)
68+
public String self(
69+
// @ModelAttribute will load User from session but also set values from the form post
70+
@Validated(User.UserUpdate.class) @ModelAttribute(ATTRIBUTE_NAME) User user,
71+
BindingResult bindingResult,
72+
RedirectAttributes redirectAttributes,
73+
// SessionStatus lets you clear your SessionAttributes
74+
SessionStatus sessionStatus
75+
){
76+
77+
logger.info("Usuario a actualizar: " + user.toString());
78+
if(!bindingResult.hasErrors()) {
79+
try {
80+
userService.update(user);
81+
} catch(UserAlredyExistsException e){
82+
bindingResult.rejectValue("email", "user.exists");
83+
}
84+
}
85+
86+
if(bindingResult.hasErrors()) {
87+
//put the validation errors in Flash session and redirect to self
88+
redirectAttributes.addFlashAttribute(BINDING_RESULT_NAME, bindingResult);
89+
return "redirect:/admin/users/self";
90+
}
91+
92+
sessionStatus.setComplete(); //remove user from session
93+
94+
List<String> successMessages = new ArrayList();
95+
successMessages.add(messageSource.getMessage("message.profile.save.success", new Object[]{ }, Locale.getDefault()));
96+
redirectAttributes.addFlashAttribute("successFlashMessages", successMessages);
97+
return "redirect:/admin/users/self";
98+
}
99+
}

src/main/java/controllers/admin/SignupController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import services.UserService;
2727
import java.util.List;
2828
import java.util.ArrayList;
29+
import org.springframework.validation.annotation.Validated;
2930
/**
3031
*
3132
* @author sergio
@@ -50,7 +51,7 @@ public String showSignupForm(Model model){
5051
}
5152

5253
@PostMapping("/signup")
53-
public String processSignup(@ModelAttribute @Valid User user, Errors errors, RedirectAttributes model){
54+
public String processSignup(@Validated(User.UserCreation.class) @ModelAttribute User user, Errors errors, RedirectAttributes model){
5455
String viewName = "admin/signup";
5556
if(!errors.hasErrors()){
5657
try {

src/main/java/controllers/admin/UsersController.java

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,14 @@
55
*/
66
package controllers.admin;
77

8-
import exceptions.UserAlredyExistsException;
9-
import java.util.ArrayList;
108
import java.util.List;
11-
import java.util.Locale;
12-
import javax.validation.Valid;
139
import models.User;
1410
import org.springframework.beans.factory.annotation.Autowired;
15-
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
1611
import org.springframework.stereotype.Controller;
1712
import org.springframework.ui.Model;
18-
import org.springframework.validation.Errors;
1913
import org.springframework.web.bind.annotation.GetMapping;
20-
import org.springframework.web.bind.annotation.ModelAttribute;
21-
import org.springframework.web.bind.annotation.PostMapping;
2214
import org.springframework.web.bind.annotation.RequestMapping;
23-
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
2415
import services.UserService;
25-
import services.security.CurrentUser;
2616

2717
/**
2818
*
@@ -34,31 +24,6 @@ public class UsersController {
3424

3525
@Autowired
3626
private UserService userService;
37-
@Autowired
38-
private ReloadableResourceBundleMessageSource messageSource;
39-
40-
@GetMapping("/self")
41-
public String self(@CurrentUser User activeUser, Model model){
42-
model.addAttribute("user", activeUser);
43-
return "admin/user/self";
44-
}
45-
46-
@PostMapping("/self")
47-
public String self(@ModelAttribute @Valid User user, Errors errors, RedirectAttributes model){
48-
String viewName = "admin/user/self";
49-
if(!errors.hasErrors()){
50-
try{
51-
userService.update(user);
52-
List<String> successMessages = new ArrayList();
53-
successMessages.add(messageSource.getMessage("message.profile.save.success", new Object[]{ }, Locale.getDefault()));
54-
model.addFlashAttribute("successFlashMessages", successMessages);
55-
viewName = "redirect:/admin/user/self";
56-
}catch(UserAlredyExistsException e){
57-
errors.rejectValue("email", "user.exists");
58-
}
59-
}
60-
return viewName;
61-
}
6227

6328
@GetMapping("/all")
6429
public String all(Model model){

src/main/java/handlers/CustomLoginSuccessHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,4 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
5656
super.onAuthenticationSuccess(request, response, authentication);
5757
}
5858
}
59-
6059
}

src/main/java/models/User.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,42 @@
2828
@Table(name = "users")
2929
@FieldMatch(first = "passwordClear", second = "confirmPassword", message = "{user.pass.not.match}")
3030
public class User implements Serializable, UserDetails {
31+
32+
/* Marker interface for grouping validations to be applied at the time of creating a (new) user. */
33+
public interface UserCreation{}
34+
/* Marker interface for grouping validations to be applied at the time of updating a (existing) user. */
35+
public interface UserUpdate{}
3136

3237
@Id
3338
@GeneratedValue(strategy = GenerationType.IDENTITY)
3439
private Long id;
3540

36-
@NotBlank(message="{user.username.notnull}")
37-
@Size(min=5, max=15, message="{user.username.size}")
41+
@NotBlank(message="{user.username.notnull}", groups={ UserCreation.class, UserUpdate.class })
42+
@Size(min=5, max=15, message="{user.username.size}", groups={ UserCreation.class, UserUpdate.class })
3843
@Column(nullable = false, length = 30, unique = true)
3944
private String username;
4045

41-
@NotBlank(message="{user.pass.notnull}")
42-
@Size(min=8, max=25, message="{user.pass.size}")
46+
@NotBlank(message="{user.pass.notnull}", groups={ UserCreation.class })
47+
@Size(min=8, max=25, message="{user.pass.size}", groups={ UserCreation.class })
4348
@Transient
4449
private String passwordClear;
4550

46-
@NotBlank(message="{user.confirm.pass.notnull}")
51+
@NotBlank(message="{user.confirm.pass.notnull}", groups={ UserCreation.class })
4752
@Transient
4853
private String confirmPassword;
4954

5055
@Column(length = 60)
5156
private String password;
5257

53-
@NotBlank(message="{user.email.notnull}")
54-
@Email(message="{user.email.invalid}")
58+
@NotBlank(message="{user.email.notnull}", groups={ UserCreation.class, UserUpdate.class })
59+
@Email(message="{user.email.invalid}", groups={ UserCreation.class, UserUpdate.class })
5560
@Column(nullable = false, length = 90, unique = true)
5661
private String email;
5762

5863
private Boolean enabled = true;
5964

60-
@NotBlank(message="{user.fullname.notnull}")
61-
@Size(min=8, max=25, message="{user.fullname.size}")
65+
@NotBlank(message="{user.fullname.notnull}", groups={ UserCreation.class, UserUpdate.class })
66+
@Size(min=8, max=25, message="{user.fullname.size}", groups={ UserCreation.class, UserUpdate.class })
6267
@Column(length = 100)
6368
private String fullName;
6469

@@ -75,6 +80,7 @@ public class User implements Serializable, UserDetails {
7580
joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
7681
inverseJoinColumns=@JoinColumn(name="ROLE_ID", referencedColumnName="ID"))
7782
private Set<Role> roles = new HashSet();
83+
7884

7985
public User() {}
8086

src/main/java/repositories/UserRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public interface UserRepository extends JpaRepository<User, Long> {
1313
User findByUsername(String username);
1414
@Query("SELECT COUNT(u.id) FROM User u WHERE u.email=:email OR u.username=:username")
1515
Long existsUserWithEmailOrUsername(@Param("email") String email, @Param("username") String username);
16+
@Query("SELECT COUNT(u.id) FROM User u WHERE u.id!=:id AND (u.email=:email OR u.username=:username)")
17+
Long existsUserWithEmailOrUsernameAndNotId(@Param("email") String email, @Param("username") String username, @Param("id") Long id);
1618
@Modifying(clearAutomatically = true)
1719
@Query("update User user set user.lastLoginAccess =:lastLoginAccess where user.username =:username")
1820
@Transactional

src/main/java/services/UserServiceImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* @author sergio
2424
*/
2525
@Service
26+
@Transactional
2627
public class UserServiceImpl implements UserService {
2728

2829
private static Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@@ -31,7 +32,6 @@ public class UserServiceImpl implements UserService {
3132
@Autowired
3233
private PasswordEncoder passwordEncoder;
3334

34-
@Transactional
3535
@Override
3636
public void create(User user) throws UserAlredyExistsException {
3737
logger.debug("Registrando nuevo usuario: " + user.getUsername());
@@ -42,10 +42,9 @@ public void create(User user) throws UserAlredyExistsException {
4242
userRepository.save(user);
4343
}
4444

45-
@Transactional
4645
@Override
4746
public void update(User user) throws UserAlredyExistsException {
48-
if (userRepository.existsUserWithEmailOrUsername(user.getEmail(), user.getUsername()) > 0){
47+
if (userRepository.existsUserWithEmailOrUsernameAndNotId(user.getEmail(), user.getUsername(), user.getId()) > 0){
4948
throw new UserAlredyExistsException(user.getEmail(), user.getUsername());
5049
}
5150
userRepository.save(user);

src/main/webapp/WEB-INF/templates/admin/fragment/alerts.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,10 @@
1111
<li th:each="err: ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li>
1212
</ul>
1313
</div>
14+
<div th:fragment="flash-message" th:if="${not #lists.isEmpty(successFlashMessages)}" class="alert alert-success">
15+
<ul>
16+
<li th:each="message:${successFlashMessages}" th:text="${message}"></li>
17+
</ul>
18+
</div>
1419
</body>
1520
</html>

src/main/webapp/WEB-INF/templates/admin/fragment/post.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<body>
99
<form th:fragment="form(target, delete)" accept-charset="UTF-8" enctype="multipart/form-data" id="postForm" name="postForm" method="post" action="#" th:action="${target}" th:object="${post}">
1010
<th:block th:replace="admin/fragment/alerts::form-errors"></th:block>
11+
<th:block th:replace="admin/fragment/alerts::flash-message"></th:block>
1112
<div class="row control-group">
1213
<div class="form-group col-xs-12 floating-label-form-group controls">
1314
<label th:text="#{form.post.title.label}">Title</label>

0 commit comments

Comments
 (0)