Merge branch 'integ'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,5 +25,5 @@ nbdist/
|
|||||||
|
|
||||||
### Angular ###
|
### Angular ###
|
||||||
src/main/resources/static/
|
src/main/resources/static/
|
||||||
src/main/ts/node_modules/
|
**/node_modules/
|
||||||
node/
|
node/
|
||||||
Binary file not shown.
13
pom.xml
13
pom.xml
@@ -33,10 +33,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- <dependency> -->
|
<dependency>
|
||||||
<!-- <groupId>org.springframework.boot</groupId> -->
|
<groupId>org.springframework.boot</groupId>
|
||||||
<!-- <artifactId>spring-boot-starter-security</artifactId> -->
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
<!-- </dependency> -->
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -48,6 +48,11 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.mindrot/jbcrypt -->
|
<!-- https://mvnrepository.com/artifact/org.mindrot/jbcrypt -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mindrot</groupId>
|
<groupId>org.mindrot</groupId>
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package org.codiki;
|
|||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
|
@ComponentScan(basePackages = "org.codiki")
|
||||||
public class CodikiApplication {
|
public class CodikiApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.codiki.account;
|
package org.codiki.account;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -8,9 +9,14 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import org.codiki.core.entities.dto.PasswordWrapperDTO;
|
import org.codiki.core.entities.dto.PasswordWrapperDTO;
|
||||||
import org.codiki.core.entities.dto.UserDTO;
|
import org.codiki.core.entities.dto.UserDTO;
|
||||||
|
import org.codiki.core.entities.dto.View;
|
||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.security.TokenService;
|
import org.codiki.core.services.UserService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
@@ -18,43 +24,28 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/account")
|
@RequestMapping("/api/account")
|
||||||
public class AccountController {
|
public class AccountController {
|
||||||
|
|
||||||
private static final String HEADER_TOKEN = "token";
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AccountService accountService;
|
private AccountService accountService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private UserService userService;
|
||||||
|
|
||||||
/**
|
@JsonView(View.UserDTO.class)
|
||||||
* Log in the user in request body.
|
|
||||||
*
|
|
||||||
* @param pUser
|
|
||||||
* The user to connect.
|
|
||||||
* @param response
|
|
||||||
* The reponse injected by Spring.
|
|
||||||
* @return The connected user object.
|
|
||||||
* @throws IOException
|
|
||||||
* If credentials are bad.
|
|
||||||
*/
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public UserDTO login(@RequestBody UserDTO pUser, HttpServletResponse response) throws IOException {
|
public User login(@RequestBody final User pUser) throws BadCredentialsException {
|
||||||
return accountService.checkCredentials(response, pUser);
|
return accountService.authenticate(pUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log out the user.
|
|
||||||
*
|
|
||||||
* @param pRequest
|
|
||||||
* The request injected by Spring.
|
|
||||||
*/
|
|
||||||
@GetMapping("/logout")
|
@GetMapping("/logout")
|
||||||
public void logout(HttpServletRequest pRequest) {
|
public void logout(final HttpServletRequest request, final HttpServletResponse response) {
|
||||||
tokenService.removeUser(pRequest.getHeader(HEADER_TOKEN));
|
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if(auth != null) {
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response, auth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,8 +65,9 @@ public class AccountController {
|
|||||||
@PutMapping("/changePassword")
|
@PutMapping("/changePassword")
|
||||||
public void changePassword(@RequestBody final PasswordWrapperDTO pPasswordWrapper,
|
public void changePassword(@RequestBody final PasswordWrapperDTO pPasswordWrapper,
|
||||||
final HttpServletRequest pRequest,
|
final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse,
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Principal pPrincipal) throws IOException {
|
||||||
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
accountService.changePassword(connectedUser.get(), pPasswordWrapper, pResponse);
|
accountService.changePassword(connectedUser.get(), pPasswordWrapper, pResponse);
|
||||||
} else {
|
} else {
|
||||||
@@ -84,13 +76,13 @@ public class AccountController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/signin")
|
@PostMapping("/signin")
|
||||||
public UserDTO signin(@RequestBody final UserDTO pUser, final HttpServletResponse pResponse) throws IOException {
|
public void signin(@RequestBody final User pUser, final HttpServletResponse pResponse) throws IOException {
|
||||||
return accountService.signin(pUser, pResponse);
|
accountService.signin(pUser, pResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/")
|
@PutMapping("/")
|
||||||
public void update(@RequestBody final UserDTO pUser, final HttpServletRequest pRequest,
|
public void update(@RequestBody final UserDTO pUser, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
accountService.updateUser(pUser, pRequest, pResponse);
|
accountService.updateUser(pUser, pRequest, pResponse, pPrincipal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package org.codiki.account;
|
package org.codiki.account;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.naming.AuthenticationException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@@ -17,11 +17,14 @@ import org.codiki.core.entities.dto.UserDTO;
|
|||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.repositories.ImageRepository;
|
import org.codiki.core.repositories.ImageRepository;
|
||||||
import org.codiki.core.repositories.UserRepository;
|
import org.codiki.core.repositories.UserRepository;
|
||||||
import org.codiki.core.security.TokenService;
|
import org.codiki.core.security.CustomAuthenticationProvider;
|
||||||
import org.codiki.core.services.FileUploadService;
|
import org.codiki.core.services.FileUploadService;
|
||||||
|
import org.codiki.core.services.UserService;
|
||||||
import org.codiki.core.utils.StringUtils;
|
import org.codiki.core.utils.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@@ -29,10 +32,13 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
public class AccountService {
|
public class AccountService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private CustomAuthenticationProvider authenticationProvider;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FileUploadService fileUploadService;
|
private FileUploadService fileUploadService;
|
||||||
@@ -40,30 +46,12 @@ public class AccountService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ImageRepository imageRepository;
|
private ImageRepository imageRepository;
|
||||||
|
|
||||||
/**
|
public User authenticate(final User pUser) throws BadCredentialsException {
|
||||||
* Check the user credentials and generate him a token if they are correct.
|
final User user = userService.checkCredentials(pUser.getEmail(), pUser.getPassword());
|
||||||
*
|
|
||||||
* @param pUser
|
|
||||||
* The user sent from client.
|
|
||||||
* @return The user populated with the generated token.
|
|
||||||
* @throws IOException
|
|
||||||
* If the credentials are bad.
|
|
||||||
* @throws AuthenticationException
|
|
||||||
* If the credentials are wrong.
|
|
||||||
*/
|
|
||||||
public UserDTO checkCredentials(HttpServletResponse pResponse, UserDTO pUser) throws IOException {
|
|
||||||
UserDTO result = null;
|
|
||||||
|
|
||||||
Optional<User> user = userRepository.findByEmail(pUser.getEmail());
|
authenticationProvider.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
|
||||||
|
|
||||||
if(user.isPresent() && StringUtils.compareHash(pUser.getPassword(), user.get().getPassword())) {
|
return user;
|
||||||
tokenService.addUser(user.get());
|
|
||||||
result = new UserDTO(user.get(), true);
|
|
||||||
} else {
|
|
||||||
pResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changePassword(final User pUser, final PasswordWrapperDTO pPasswordWrapper,
|
public void changePassword(final User pUser, final PasswordWrapperDTO pPasswordWrapper,
|
||||||
@@ -85,11 +73,11 @@ public class AccountService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String uploadFile(final MultipartFile pFile,
|
public String uploadFile(final MultipartFile pFile, final HttpServletRequest pRequest,
|
||||||
final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
final String avatarFileName = fileUploadService.uploadProfileImage(pFile);
|
final String avatarFileName = fileUploadService.uploadProfileImage(pFile);
|
||||||
|
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
|
final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
|
||||||
if(userFromDb.isPresent()) {
|
if(userFromDb.isPresent()) {
|
||||||
@@ -109,10 +97,11 @@ public class AccountService {
|
|||||||
return fileUploadService.loadAvatar(pAvatarFileName);
|
return fileUploadService.loadAvatar(pAvatarFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImageDTO> getUserImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
public List<ImageDTO> getUserImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
|
||||||
|
final Principal pPrincipal) throws IOException {
|
||||||
List<ImageDTO> result = new LinkedList<>();
|
List<ImageDTO> result = new LinkedList<>();
|
||||||
|
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
result = imageRepository.getByUserId(connectedUser.get().getId())
|
result = imageRepository.getByUserId(connectedUser.get().getId())
|
||||||
.stream().map(ImageDTO::new).collect(Collectors.toList());
|
.stream().map(ImageDTO::new).collect(Collectors.toList());
|
||||||
@@ -123,27 +112,25 @@ public class AccountService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDTO signin(final UserDTO pUser, final HttpServletResponse pResponse) throws IOException {
|
public void signin(final User pUser, final HttpServletResponse pResponse) throws IOException {
|
||||||
User user = new User();
|
|
||||||
|
|
||||||
if(pUser.getName() == null || pUser.getEmail() == null || pUser.getPassword() == null || "".equals(pUser.getPassword().trim())) {
|
if(pUser.getName() == null || pUser.getEmail() == null || pUser.getPassword() == null || "".equals(pUser.getPassword().trim())) {
|
||||||
pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
} else if(userRepository.findByEmail(pUser.getEmail()).isPresent()) {
|
} else if(userRepository.findByEmail(pUser.getEmail()).isPresent()) {
|
||||||
pResponse.sendError(HttpServletResponse.SC_CONFLICT);
|
pResponse.sendError(HttpServletResponse.SC_CONFLICT);
|
||||||
} else {
|
} else {
|
||||||
|
User user = new User();
|
||||||
|
|
||||||
user.setName(pUser.getName());
|
user.setName(pUser.getName());
|
||||||
user.setEmail(pUser.getEmail());
|
user.setEmail(pUser.getEmail());
|
||||||
user.setPassword(StringUtils.hashPassword(pUser.getPassword()));
|
user.setPassword(StringUtils.hashPassword(pUser.getPassword()));
|
||||||
user.setInscriptionDate(new Date());
|
user.setInscriptionDate(new Date());
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UserDTO(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateUser(final UserDTO pUser, final HttpServletRequest pRequest,
|
public void updateUser(final UserDTO pUser, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
final Optional<User> connectedUserOpt = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUserOpt = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUserOpt.isPresent()) {
|
if(connectedUserOpt.isPresent()) {
|
||||||
final User connectedUser = connectedUserOpt.get();
|
final User connectedUser = connectedUserOpt.get();
|
||||||
|
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
package org.codiki.core;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.FilterConfig;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.codiki.core.security.Route;
|
|
||||||
import org.codiki.core.utils.StringUtils;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all filters of the application.<br/>
|
|
||||||
* <br/>
|
|
||||||
* The children classes have to implements the method
|
|
||||||
* {@link AbstractFilter#getClass()} to set the URLs filtered (with all or some
|
|
||||||
* http methods), and the method
|
|
||||||
* {@link AbstractFilter#filter(HttpServletRequest, ServletResponse, FilterChain)}
|
|
||||||
* to define the filter processing.
|
|
||||||
*
|
|
||||||
* @author Takiguchi
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class AbstractFilter implements Filter {
|
|
||||||
|
|
||||||
/** Regex url path prefix for method {@link this#isRequestFiltered(String)}. */
|
|
||||||
private static final String PREFIX_URL_PATH = "https?:\\/\\/.*(:\\d{0,5})?";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the list of routes which will be processed by the filter.
|
|
||||||
*
|
|
||||||
* @return The routes.
|
|
||||||
*/
|
|
||||||
protected abstract List<Route> getRoutes();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter actions for its processing.
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
* The http request.
|
|
||||||
* @param response
|
|
||||||
* The response.
|
|
||||||
* @param chain
|
|
||||||
* The chain.
|
|
||||||
*/
|
|
||||||
protected abstract void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
|
||||||
|
|
||||||
if(isRequestFiltered(httpRequest.getRequestURL().toString(), httpRequest.getMethod())) {
|
|
||||||
filter(httpRequest, (HttpServletResponse) response, chain);
|
|
||||||
} else {
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the url is allowed with the given method in parameters.
|
|
||||||
*
|
|
||||||
* @param pRequestUrl
|
|
||||||
* The url request.
|
|
||||||
* @param pRequestMethod
|
|
||||||
* The http method of the request.
|
|
||||||
* @return {@code true} if the url is allowed with the method, {@code false}
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
boolean isRequestFiltered(final String pRequestUrl, final String pRequestMethod) {
|
|
||||||
boolean result = false;
|
|
||||||
|
|
||||||
for(final Route route : getRoutes()) {
|
|
||||||
/*
|
|
||||||
* Check urls matching, and if the method of the route isn't set, all methods
|
|
||||||
* are allowed. Otherwise, we check the methods too.
|
|
||||||
*/
|
|
||||||
if(Pattern.matches(StringUtils.concat(PREFIX_URL_PATH, route.getUrl()), pRequestUrl)) {
|
|
||||||
if(!route.getMethod().isPresent() || isMethodFiltered(route, pRequestMethod)) {
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the route do filter the method in parameters.
|
|
||||||
*
|
|
||||||
* @param pRoute
|
|
||||||
* The registered route.
|
|
||||||
* @param pRequestMethod
|
|
||||||
* The http method to check with the registered route.
|
|
||||||
*/
|
|
||||||
boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) {
|
|
||||||
boolean result = false;
|
|
||||||
|
|
||||||
if(pRoute.getMethod().isPresent()) {
|
|
||||||
for(final HttpMethod routeMethod : pRoute.getMethod().get()) {
|
|
||||||
if(routeMethod.name().equals(pRequestMethod)) {
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.codiki.core.constant;
|
|
||||||
|
|
||||||
public enum FileEnum {
|
|
||||||
/** Folder in where pictures will be uploaded. */
|
|
||||||
FOLDER_UPLOAD("/opt/codiki/pictures/tmp/"),
|
|
||||||
/** Folder in where profile pictures will be stored. */
|
|
||||||
FOLDER_PROFILE_IMAGES("/opt/codiki/pictures/profiles/"),
|
|
||||||
/** Folder in where images will be stored. */
|
|
||||||
FOLDER_IMAGE("/opt/codiki/pictures/posts/");
|
|
||||||
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
private FileEnum(final String pValue) {
|
|
||||||
value = pValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String val() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,8 +21,6 @@ public class UserDTO {
|
|||||||
|
|
||||||
private Role role;
|
private Role role;
|
||||||
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
public UserDTO() {
|
public UserDTO() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -38,9 +36,6 @@ public class UserDTO {
|
|||||||
|
|
||||||
public UserDTO(final User pUser, final boolean pWithToken) {
|
public UserDTO(final User pUser, final boolean pWithToken) {
|
||||||
this(pUser);
|
this(pUser);
|
||||||
if(pWithToken) {
|
|
||||||
token = pUser.getToken().getValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
@@ -98,12 +93,4 @@ public class UserDTO {
|
|||||||
public void setRole(Role role) {
|
public void setRole(Role role) {
|
||||||
this.role = role;
|
this.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToken(String token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/main/java/org/codiki/core/entities/dto/View.java
Executable file
6
src/main/java/org/codiki/core/entities/dto/View.java
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
package org.codiki.core.entities.dto;
|
||||||
|
|
||||||
|
public class View {
|
||||||
|
public interface UserDTO {}
|
||||||
|
public interface PostDTO {}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
@@ -16,6 +17,9 @@ import javax.persistence.OneToMany;
|
|||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
import org.codiki.core.entities.dto.CategoryDTO;
|
import org.codiki.core.entities.dto.CategoryDTO;
|
||||||
|
import org.codiki.core.entities.dto.View;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="category")
|
@Table(name="category")
|
||||||
@@ -28,14 +32,16 @@ public class Category implements Serializable {
|
|||||||
/* ******************* */
|
/* ******************* */
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
/* Relations */
|
/* Relations */
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "creator_id")
|
@JoinColumn(name = "creator_id")
|
||||||
protected User creator;
|
protected User creator;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
@@ -39,7 +40,7 @@ public class Comment implements Serializable {
|
|||||||
/* ******************* */
|
/* ******************* */
|
||||||
/* Relations */
|
/* Relations */
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "author")
|
@JoinColumn(name = "author")
|
||||||
private User author;
|
private User author;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.Date;
|
|||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
@@ -35,7 +36,7 @@ public class CommentHistory implements Serializable {
|
|||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date updateDate;
|
private Date updateDate;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "comment_id")
|
@JoinColumn(name = "comment_id")
|
||||||
private Comment comment;
|
private Comment comment;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.io.Serializable;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
@@ -30,7 +31,7 @@ public class Image implements Serializable {
|
|||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date date;
|
private Date date;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "user_id")
|
@JoinColumn(name = "user_id")
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ import javax.persistence.Temporal;
|
|||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
import org.codiki.core.entities.dto.PostDTO;
|
import org.codiki.core.entities.dto.PostDTO;
|
||||||
|
import org.codiki.core.entities.dto.View;
|
||||||
import org.codiki.core.utils.DateUtils;
|
import org.codiki.core.utils.DateUtils;
|
||||||
import org.hibernate.annotations.Generated;
|
import org.hibernate.annotations.Generated;
|
||||||
import org.hibernate.annotations.GenerationTime;
|
import org.hibernate.annotations.GenerationTime;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="post")
|
@Table(name="post")
|
||||||
public class Post implements Serializable {
|
public class Post implements Serializable {
|
||||||
@@ -38,19 +41,25 @@ public class Post implements Serializable {
|
|||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
// This annotation serves to fetch the attribute after an insert into db
|
// This annotation serves to fetch the attribute after an insert into db
|
||||||
@Generated(GenerationTime.ALWAYS)
|
@Generated(GenerationTime.ALWAYS)
|
||||||
private String key;
|
private String key;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
private String text;
|
private String text;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@Column(length = 250)
|
@Column(length = 250)
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
private String image;
|
private String image;
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@Column(name = "creation_date")
|
@Column(name = "creation_date")
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date creationDate;
|
private Date creationDate;
|
||||||
@@ -58,11 +67,13 @@ public class Post implements Serializable {
|
|||||||
/* ******************* */
|
/* ******************* */
|
||||||
/* Relations */
|
/* Relations */
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
@JsonView(View.PostDTO.class)
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "creator_id")
|
@JoinColumn(name = "creator_id")
|
||||||
private User author;
|
private User author;
|
||||||
|
|
||||||
@ManyToOne
|
@JsonView(View.PostDTO.class)
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "category_id")
|
@JoinColumn(name = "category_id")
|
||||||
private Category category;
|
private Category category;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.Date;
|
|||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
@@ -31,7 +32,7 @@ public class PostHistory implements Serializable {
|
|||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date updateDate;
|
private Date updateDate;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "post_id")
|
@JoinColumn(name = "post_id")
|
||||||
private Post post;
|
private Post post;
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,21 @@ import javax.persistence.GenerationType;
|
|||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.codiki.core.entities.dto.View;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="role")
|
@Table(name="role")
|
||||||
public class Role implements Serializable {
|
public class Role implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class})
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class})
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.codiki.core.entities.persistence;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
@@ -15,7 +16,7 @@ public class SubCategory extends Category {
|
|||||||
/* ******************* */
|
/* ******************* */
|
||||||
/* Relations */
|
/* Relations */
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "main_category")
|
@JoinColumn(name = "main_category")
|
||||||
private Category mainCategory;
|
private Category mainCategory;
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ import javax.persistence.Table;
|
|||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
import org.codiki.core.entities.security.Token;
|
import org.codiki.core.entities.dto.View;
|
||||||
import org.hibernate.annotations.Generated;
|
import org.hibernate.annotations.Generated;
|
||||||
import org.hibernate.annotations.GenerationTime;
|
import org.hibernate.annotations.GenerationTime;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="`user`")
|
@Table(name="`user`")
|
||||||
public class User implements Serializable {
|
public class User implements Serializable {
|
||||||
@@ -37,18 +39,23 @@ public class User implements Serializable {
|
|||||||
@SequenceGenerator(name="user_id_seq", sequenceName="user_id_seq", allocationSize=1)
|
@SequenceGenerator(name="user_id_seq", sequenceName="user_id_seq", allocationSize=1)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class, View.PostDTO.class})
|
||||||
// This annotation serves to fetch the attribute after an insert into db
|
// This annotation serves to fetch the attribute after an insert into db
|
||||||
@Generated(GenerationTime.ALWAYS)
|
@Generated(GenerationTime.ALWAYS)
|
||||||
private String key;
|
private String key;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class, View.PostDTO.class})
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class, View.PostDTO.class})
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class, View.PostDTO.class})
|
||||||
private String image;
|
private String image;
|
||||||
|
|
||||||
|
@JsonView({View.UserDTO.class, View.PostDTO.class})
|
||||||
@Column(name = "inscription_date")
|
@Column(name = "inscription_date")
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date inscriptionDate;
|
private Date inscriptionDate;
|
||||||
@@ -56,7 +63,8 @@ public class User implements Serializable {
|
|||||||
/* ******************* */
|
/* ******************* */
|
||||||
/* Relations */
|
/* Relations */
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
@JsonView({View.UserDTO.class})
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "role_id")
|
@JoinColumn(name = "role_id")
|
||||||
private Role role;
|
private Role role;
|
||||||
|
|
||||||
@@ -69,15 +77,11 @@ public class User implements Serializable {
|
|||||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
|
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
|
||||||
private List<Image> listImages;
|
private List<Image> listImages;
|
||||||
|
|
||||||
/** Authentication token. */
|
|
||||||
private transient Token token;
|
|
||||||
|
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
/* Constructors */
|
/* Constructors */
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
public User() {
|
public User() {
|
||||||
super();
|
super();
|
||||||
token = new Token();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ******************* */
|
/* ******************* */
|
||||||
@@ -184,9 +188,4 @@ public class User implements Serializable {
|
|||||||
public void setListImages(List<Image> listImages) {
|
public void setListImages(List<Image> listImages) {
|
||||||
this.listImages = listImages;
|
this.listImages = listImages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.codiki.core.entities.persistence.Post;
|
import org.codiki.core.entities.persistence.Post;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
@@ -15,9 +16,9 @@ public interface PostRepository extends CrudRepository<Post, Long>, PostSearchRe
|
|||||||
@Query("SELECT p FROM Post p WHERE p.key = :postKey")
|
@Query("SELECT p FROM Post p WHERE p.key = :postKey")
|
||||||
Optional<Post> getByKey(@Param("postKey") final String pPostKey);
|
Optional<Post> getByKey(@Param("postKey") final String pPostKey);
|
||||||
|
|
||||||
@Query(value = "SELECT * FROM post p INNER JOIN \"user\" u ON u.id = p.creator_id ORDER BY p.creation_date DESC LIMIT :limit",
|
// TODO : Remove "JOIN FETCH u.role", actually necessary for JSON serialization
|
||||||
nativeQuery = true)
|
@Query("SELECT p FROM Post p JOIN FETCH p.author u JOIN FETCH u.role ORDER BY p.creationDate DESC")
|
||||||
List<Post> getLast(@Param("limit") final Integer pLimit);
|
List<Post> getLast(Pageable pPageable);
|
||||||
|
|
||||||
@Query(value = "SELECT * FROM post p INNER JOIN \"user\" u ON u.id = creator_id WHERE category_id = :categoryId ORDER BY creation_date DESC",
|
@Query(value = "SELECT * FROM post p INNER JOIN \"user\" u ON u.id = creator_id WHERE category_id = :categoryId ORDER BY creation_date DESC",
|
||||||
nativeQuery = true)
|
nativeQuery = true)
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
package org.codiki.core.security;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.codiki.core.AbstractFilter;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.core.Ordered;
|
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
|
||||||
public class AuthenticationFilter extends AbstractFilter {
|
|
||||||
|
|
||||||
private static final String HTTP_OPTIONS = "OPTIONS";
|
|
||||||
|
|
||||||
private static final String HEADER_TOKEN = "token";
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TokenService tokenService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<Route> getRoutes() {
|
|
||||||
return Arrays.asList(
|
|
||||||
new Route("\\/api\\/posts\\/myPosts"),
|
|
||||||
new Route("\\/api\\/posts\\/preview"),
|
|
||||||
new Route("\\/api\\/posts\\/", HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE),
|
|
||||||
new Route("\\/api\\/account\\/changePassword"),
|
|
||||||
new Route("\\/api\\/account\\/", HttpMethod.PUT)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
|
||||||
if(HTTP_OPTIONS.equals(request.getMethod()) || tokenService.isUserConnected(request.getHeader(HEADER_TOKEN))) {
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
} else {
|
|
||||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
33
src/main/java/org/codiki/core/security/CustomAuthenticationProvider.java
Executable file
33
src/main/java/org/codiki/core/security/CustomAuthenticationProvider.java
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
package org.codiki.core.security;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class CustomAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
|
// Creation of the authentication bean with its roles
|
||||||
|
Authentication auth = new UsernamePasswordAuthenticationToken(authentication.getName(),
|
||||||
|
authentication.getCredentials(), new ArrayList<GrantedAuthority>());
|
||||||
|
|
||||||
|
// Set the auth bean in spring security context
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
|
|
||||||
|
return auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> authentication) {
|
||||||
|
return authentication.equals(UsernamePasswordAuthenticationToken.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
src/main/java/org/codiki/core/security/RestAuthenticationEntryPoint.java
Executable file
28
src/main/java/org/codiki/core/security/RestAuthenticationEntryPoint.java
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
package org.codiki.core.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication entry point configured in
|
||||||
|
* {@link SecurityConfiguration#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)}
|
||||||
|
* to avoid yo get a login form at authentication failure from Angular app.
|
||||||
|
*
|
||||||
|
* @author takiguchi
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
AuthenticationException authException) throws IOException, ServletException {
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package org.codiki.core.security;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Route for filter matching.
|
|
||||||
*
|
|
||||||
* @author Takiguchi
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Route {
|
|
||||||
/** The regex to match urls. */
|
|
||||||
private String url;
|
|
||||||
/** The http method to match. Use a {@link Optional#empty()} to match all methods. */
|
|
||||||
private Optional<List<HttpMethod>> method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instanciate a vierge route.
|
|
||||||
*/
|
|
||||||
public Route() {
|
|
||||||
super();
|
|
||||||
url = "";
|
|
||||||
method = Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instanciate a route for all http methods.
|
|
||||||
*
|
|
||||||
* @param pUrl
|
|
||||||
* The regex to match urls.
|
|
||||||
*/
|
|
||||||
public Route(final String pUrl) {
|
|
||||||
this();
|
|
||||||
this.url = pUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instanciate a route for methods given in parameters
|
|
||||||
*
|
|
||||||
* @param pUrl
|
|
||||||
* The regex to match urls.
|
|
||||||
* @param pMethod
|
|
||||||
* The http method to match. Use a {@link Optional#empty()} to match
|
|
||||||
* all methods.
|
|
||||||
*/
|
|
||||||
public Route(final String pUrl, final HttpMethod... pMethods) {
|
|
||||||
this(pUrl);
|
|
||||||
this.method = Optional.of(Arrays.asList(pMethods));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUrl(String url) {
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<List<HttpMethod>> getMethod() {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMethod(HttpMethod pMethods) {
|
|
||||||
this.method = Optional.of(Arrays.asList(pMethods));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
75
src/main/java/org/codiki/core/security/SecurityConfiguration.java
Executable file
75
src/main/java/org/codiki/core/security/SecurityConfiguration.java
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
package org.codiki.core.security;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
|
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||||
|
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
@Order(SecurityProperties.BASIC_AUTH_ORDER)
|
||||||
|
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
private static final String XSRF_REPOSITORY_HEADER_NAME = "X-XSRF-TOKEN";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CustomAuthenticationProvider authenticationProvider;
|
||||||
|
@Autowired
|
||||||
|
private RestAuthenticationEntryPoint authenticationEntryPoint;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth.authenticationProvider(authenticationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.authorizeRequests()
|
||||||
|
.antMatchers(
|
||||||
|
"/api/account/login",
|
||||||
|
"/api/account/logout",
|
||||||
|
"/api/account/signin"
|
||||||
|
).permitAll()
|
||||||
|
.antMatchers(
|
||||||
|
"/api/images/uploadAvatar",
|
||||||
|
"/api/images/myImages",
|
||||||
|
"/api/posts/myPosts"
|
||||||
|
).authenticated()
|
||||||
|
.antMatchers(
|
||||||
|
HttpMethod.GET,
|
||||||
|
"/api/categories",
|
||||||
|
"/api/images",
|
||||||
|
"/api/posts",
|
||||||
|
"/api/categories/**",
|
||||||
|
"/api/images/**",
|
||||||
|
"/api/posts/**"
|
||||||
|
).permitAll()
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
.and()
|
||||||
|
// Allow to avoid login form at authentication failure from Angular app
|
||||||
|
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
|
||||||
|
.and()
|
||||||
|
.addFilterAfter(new XSRFTokenFilter(), CsrfFilter.class)
|
||||||
|
.csrf()
|
||||||
|
.csrfTokenRepository(xsrfTokenRepository());
|
||||||
|
http.httpBasic();
|
||||||
|
http.csrf().disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsrfTokenRepository xsrfTokenRepository() {
|
||||||
|
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
|
||||||
|
repository.setHeaderName(XSRF_REPOSITORY_HEADER_NAME);
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
package org.codiki.core.security;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import org.codiki.core.entities.persistence.User;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class TokenService {
|
|
||||||
/** Map of connected users. */
|
|
||||||
private static final Map<String, User> connectedUsers;
|
|
||||||
|
|
||||||
private static final String HEADER_TOKEN = "token";
|
|
||||||
|
|
||||||
private static final long INTERVAL_USER_CLEANING = 5;
|
|
||||||
|
|
||||||
private static final long INTERVAL_USER_CLEANING_VAL = INTERVAL_USER_CLEANING * 60 * 1000;
|
|
||||||
|
|
||||||
private Date lastUsersCleaning;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class constructor
|
|
||||||
*/
|
|
||||||
static {
|
|
||||||
connectedUsers = new TreeMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the token matches to a user session, and if it is still valid.
|
|
||||||
*
|
|
||||||
* @param pToken
|
|
||||||
* The token to check.
|
|
||||||
* @return {@code true} if the token is still valid, {@code false}
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isUserConnected(final String pToken) {
|
|
||||||
boolean result = false;
|
|
||||||
|
|
||||||
if (pToken != null && connectedUsers.containsKey(pToken)) {
|
|
||||||
if (connectedUsers.get(pToken).getToken().isValid()) {
|
|
||||||
result = true;
|
|
||||||
} else {
|
|
||||||
connectedUsers.remove(pToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear all the expired sessions
|
|
||||||
final Date now = new Date();
|
|
||||||
if(lastUsersCleaning == null || now.getTime() - lastUsersCleaning.getTime() >= INTERVAL_USER_CLEANING_VAL) {
|
|
||||||
new Thread(this::clearExpiredUsers).start();
|
|
||||||
lastUsersCleaning = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove from the connected users map all the elements which their token is
|
|
||||||
* expired.
|
|
||||||
*/
|
|
||||||
private void clearExpiredUsers() {
|
|
||||||
synchronized (this) {
|
|
||||||
List<User> usersToRemove = new LinkedList<>();
|
|
||||||
connectedUsers.entrySet().stream().forEach(user -> {
|
|
||||||
if(!user.getValue().getToken().isValid()) {
|
|
||||||
usersToRemove.add(user.getValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
usersToRemove.stream().map(User::getKey).forEach(connectedUsers::remove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the user to the connected users map.
|
|
||||||
*
|
|
||||||
* @param pUser
|
|
||||||
* The user to add.
|
|
||||||
*/
|
|
||||||
public void addUser(final User pUser) {
|
|
||||||
if(connectedUsers.get(pUser.getToken().getValue()) == null) {
|
|
||||||
connectedUsers.put(pUser.getToken().getValue(), pUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh the user token last access date in the token service.
|
|
||||||
*
|
|
||||||
* @param pToken
|
|
||||||
* The user token.
|
|
||||||
*/
|
|
||||||
public void refreshUserToken(final String pToken) {
|
|
||||||
final User user = connectedUsers.get(pToken);
|
|
||||||
if(user != null) {
|
|
||||||
user.getToken().setLastAccess();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the user to the connected users map.
|
|
||||||
*
|
|
||||||
* @param pUser
|
|
||||||
* The user to remove.
|
|
||||||
*/
|
|
||||||
public void removeUser(final User pUser) {
|
|
||||||
removeUser(pUser.getToken().getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the user associated to the token given in parameters, from the
|
|
||||||
* connected users map.
|
|
||||||
*
|
|
||||||
* @param pToken
|
|
||||||
* The user to delete token.
|
|
||||||
*/
|
|
||||||
public void removeUser(final String pToken) {
|
|
||||||
if(pToken != null && connectedUsers.containsKey(pToken)) {
|
|
||||||
connectedUsers.remove(pToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<User> getAuthenticatedUserByToken(final HttpServletRequest pRequest) {
|
|
||||||
return Optional.ofNullable(connectedUsers.get(pRequest.getHeader(HEADER_TOKEN)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
34
src/main/java/org/codiki/core/security/XSRFTokenFilter.java
Executable file
34
src/main/java/org/codiki/core/security/XSRFTokenFilter.java
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
package org.codiki.core.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.security.web.csrf.CsrfToken;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
|
public class XSRFTokenFilter extends OncePerRequestFilter {
|
||||||
|
private static final String XSRF_TOKEN_PATH = "/";
|
||||||
|
private static final String XSRF_TOKEN = "XSRF-TOKEN";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
||||||
|
if(csrf != null) {
|
||||||
|
Cookie cookie = WebUtils.getCookie(request, XSRF_TOKEN);
|
||||||
|
String token = csrf.getToken();
|
||||||
|
if(cookie == null || token != null && !token.equals(cookie.getValue())) {
|
||||||
|
cookie = new Cookie(XSRF_TOKEN, token);
|
||||||
|
cookie.setPath(XSRF_TOKEN_PATH);
|
||||||
|
response.addCookie(cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,9 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.apache.commons.lang.RandomStringUtils;
|
import org.apache.commons.lang.RandomStringUtils;
|
||||||
import org.codiki.core.constant.FileEnum;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.UrlResource;
|
import org.springframework.core.io.UrlResource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -14,9 +16,18 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class FileUploadService {
|
public class FileUploadService {
|
||||||
|
/** Logger. */
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(FileUploadService.class);
|
||||||
/** Length of uploaded file names. */
|
/** Length of uploaded file names. */
|
||||||
private static final int DESTINATION_IMAGE_NAME_LENGTH = 30;
|
private static final int DESTINATION_IMAGE_NAME_LENGTH = 30;
|
||||||
|
|
||||||
|
@Value("${codiki.files.upload}")
|
||||||
|
private String folderUpload;
|
||||||
|
@Value("${codiki.files.profile-images}")
|
||||||
|
private String folderProfileImages;
|
||||||
|
@Value("${codiki.files.images}")
|
||||||
|
private String folderImages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the destination file name, which is a random string with 30 char
|
* Builds the destination file name, which is a random string with 30 char
|
||||||
* length.
|
* length.
|
||||||
@@ -28,36 +39,39 @@ public class FileUploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String uploadProfileImage(final MultipartFile pFile) {
|
public String uploadProfileImage(final MultipartFile pFile) {
|
||||||
return uploadFile(pFile, FileEnum.FOLDER_PROFILE_IMAGES);
|
return uploadFile(pFile, folderProfileImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String uploadImage(final MultipartFile pFile) {
|
public String uploadImage(final MultipartFile pFile) {
|
||||||
return uploadFile(pFile, FileEnum.FOLDER_IMAGE);
|
return uploadFile(pFile, folderImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String uploadFile(final MultipartFile pFile, final FileEnum pPath) {
|
private String uploadFile(final MultipartFile pFile, final String pPath) {
|
||||||
String result;
|
String result;
|
||||||
try {
|
try {
|
||||||
result = buildDestinationFileName();
|
result = buildDestinationFileName();
|
||||||
Files.copy(pFile.getInputStream(), Paths.get(pPath.val()).resolve(result));
|
final Path destinationFile = Paths.get(pPath).resolve(result);
|
||||||
|
LOG.debug("Upload file saved as {}", destinationFile.toString());
|
||||||
|
Files.copy(pFile.getInputStream(), destinationFile);
|
||||||
return result;
|
return result;
|
||||||
} catch (final Exception pEx) {
|
} catch (final Exception pEx) {
|
||||||
|
LOG.error("Error during file upload.", pEx);
|
||||||
// TODO : Refactor exception
|
// TODO : Refactor exception
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource loadAvatar(final String pAvatarFileName) {
|
public Resource loadAvatar(final String pAvatarFileName) {
|
||||||
return loadImage(pAvatarFileName, FileEnum.FOLDER_PROFILE_IMAGES);
|
return loadImage(pAvatarFileName, folderProfileImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource loadImage(final String pImageLink) {
|
public Resource loadImage(final String pImageLink) {
|
||||||
return loadImage(pImageLink, FileEnum.FOLDER_IMAGE);
|
return loadImage(pImageLink, folderImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Resource loadImage(final String pImageLink, final FileEnum pFilePath) {
|
private Resource loadImage(final String pImageLink, final String pFilePath) {
|
||||||
try {
|
try {
|
||||||
Path imageFile = Paths.get(pFilePath.val()).resolve(pImageLink);
|
Path imageFile = Paths.get(pFilePath).resolve(pImageLink);
|
||||||
Resource imageResource = new UrlResource(imageFile.toUri());
|
Resource imageResource = new UrlResource(imageFile.toUri());
|
||||||
if(imageResource.exists() || imageResource.isReadable()) {
|
if(imageResource.exists() || imageResource.isReadable()) {
|
||||||
return imageResource;
|
return imageResource;
|
||||||
|
|||||||
37
src/main/java/org/codiki/core/services/UserService.java
Executable file
37
src/main/java/org/codiki/core/services/UserService.java
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
package org.codiki.core.services;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.codiki.core.entities.persistence.User;
|
||||||
|
import org.codiki.core.repositories.UserRepository;
|
||||||
|
import org.codiki.core.utils.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
private static final String MSG_BAD_CREDENTIALS = "Adresse email ou mot de passe incorrect.";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
public User checkCredentials(final String email, final String password) throws BadCredentialsException {
|
||||||
|
final Optional<User> optUser = userRepository.findByEmail(email);
|
||||||
|
|
||||||
|
// If no user exists with the given email of if the given password doesn't match
|
||||||
|
// to the user password, we throw an exception.
|
||||||
|
if(!optUser.isPresent() || !StringUtils.compareHash(password, optUser.get().getPassword())) {
|
||||||
|
throw new BadCredentialsException(MSG_BAD_CREDENTIALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return optUser.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<User> getUserByPrincipal(final Principal pPrincipal) {
|
||||||
|
SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return userRepository.findByEmail(pPrincipal.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.codiki.images;
|
package org.codiki.images;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -8,6 +9,8 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import org.codiki.core.entities.dto.ImageDTO;
|
import org.codiki.core.entities.dto.ImageDTO;
|
||||||
import org.codiki.core.utils.StringUtils;
|
import org.codiki.core.utils.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@@ -24,18 +27,21 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/images")
|
@RequestMapping("/api/images")
|
||||||
public class ImageController {
|
public class ImageController {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ImageController.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ImageService imageService;
|
private ImageService imageService;
|
||||||
|
|
||||||
@PostMapping("/uploadAvatar")
|
@PostMapping("/uploadAvatar")
|
||||||
public ResponseEntity<String> uploadAvatar(@RequestParam("file") MultipartFile pFile,
|
public ResponseEntity<String> uploadAvatar(@RequestParam("file") MultipartFile pFile,
|
||||||
final HttpServletRequest pRequest, final HttpServletResponse pResponse) {
|
final HttpServletRequest pRequest, final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||||
|
LOG.debug("Upload avatar.");
|
||||||
ResponseEntity<String> result;
|
ResponseEntity<String> result;
|
||||||
try {
|
try {
|
||||||
result = ResponseEntity.status(HttpStatus.OK)
|
result = ResponseEntity.status(HttpStatus.OK)
|
||||||
.body(imageService.uploadAvatar(pFile, pRequest, pResponse));
|
.body(imageService.uploadAvatar(pFile, pRequest, pResponse, pPrincipal));
|
||||||
} catch(final Exception pEx) {
|
} catch(final Exception pEx) {
|
||||||
|
LOG.error("Error during avatar upload.", pEx);
|
||||||
result = ResponseEntity.status(HttpStatus.EXPECTATION_FAILED)
|
result = ResponseEntity.status(HttpStatus.EXPECTATION_FAILED)
|
||||||
.body(StringUtils.concat("Fail to upload ", pFile.getOriginalFilename() + "."));
|
.body(StringUtils.concat("Fail to upload ", pFile.getOriginalFilename() + "."));
|
||||||
}
|
}
|
||||||
@@ -44,11 +50,11 @@ public class ImageController {
|
|||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<String> uploadImage(@RequestParam("file") MultipartFile pFile,
|
public ResponseEntity<String> uploadImage(@RequestParam("file") MultipartFile pFile,
|
||||||
final HttpServletRequest pRequest, final HttpServletResponse pResponse) {
|
final HttpServletRequest pRequest, final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||||
ResponseEntity<String> result;
|
ResponseEntity<String> result;
|
||||||
try {
|
try {
|
||||||
result = ResponseEntity.status(HttpStatus.OK)
|
result = ResponseEntity.status(HttpStatus.OK)
|
||||||
.body(imageService.uploadImage(pFile, pRequest, pResponse));
|
.body(imageService.uploadImage(pFile, pRequest, pResponse, pPrincipal));
|
||||||
} catch(final Exception pEx) {
|
} catch(final Exception pEx) {
|
||||||
result = ResponseEntity.status(HttpStatus.EXPECTATION_FAILED)
|
result = ResponseEntity.status(HttpStatus.EXPECTATION_FAILED)
|
||||||
.body(StringUtils.concat("Fail to upload ", pFile.getOriginalFilename() + "."));
|
.body(StringUtils.concat("Fail to upload ", pFile.getOriginalFilename() + "."));
|
||||||
@@ -73,8 +79,9 @@ public class ImageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/myImages")
|
@GetMapping("/myImages")
|
||||||
public List<ImageDTO> myImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
public List<ImageDTO> myImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
|
||||||
return imageService.getUserImages(pRequest, pResponse);
|
final Principal pPrincipal) throws IOException {
|
||||||
|
return imageService.getUserImages(pRequest, pResponse, pPrincipal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{imageLink}/details")
|
@GetMapping("/{imageLink}/details")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.codiki.images;
|
package org.codiki.images;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,8 +16,8 @@ import org.codiki.core.entities.persistence.Image;
|
|||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.repositories.ImageRepository;
|
import org.codiki.core.repositories.ImageRepository;
|
||||||
import org.codiki.core.repositories.UserRepository;
|
import org.codiki.core.repositories.UserRepository;
|
||||||
import org.codiki.core.security.TokenService;
|
|
||||||
import org.codiki.core.services.FileUploadService;
|
import org.codiki.core.services.FileUploadService;
|
||||||
|
import org.codiki.core.services.UserService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -24,24 +25,23 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ImageService {
|
public class ImageService {
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TokenService tokenService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FileUploadService fileUploadService;
|
private FileUploadService fileUploadService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ImageRepository imageRepository;
|
private ImageRepository imageRepository;
|
||||||
|
|
||||||
public String uploadAvatar(final MultipartFile pFile,
|
public String uploadAvatar(final MultipartFile pFile, final HttpServletRequest pRequest,
|
||||||
final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
final String avatarFileName = fileUploadService.uploadProfileImage(pFile);
|
final String avatarFileName = fileUploadService.uploadProfileImage(pFile);
|
||||||
|
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
|
final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
|
||||||
if(userFromDb.isPresent()) {
|
if(userFromDb.isPresent()) {
|
||||||
@@ -57,11 +57,11 @@ public class ImageService {
|
|||||||
return avatarFileName;
|
return avatarFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String uploadImage(final MultipartFile pFile,
|
public String uploadImage(final MultipartFile pFile, final HttpServletRequest pRequest,
|
||||||
final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
final String imageFileName = fileUploadService.uploadImage(pFile);
|
final String imageFileName = fileUploadService.uploadImage(pFile);
|
||||||
|
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
|
final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
|
||||||
if(userFromDb.isPresent()) {
|
if(userFromDb.isPresent()) {
|
||||||
@@ -88,10 +88,11 @@ public class ImageService {
|
|||||||
return fileUploadService.loadImage(pImageLink);
|
return fileUploadService.loadImage(pImageLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImageDTO> getUserImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
public List<ImageDTO> getUserImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
|
||||||
|
final Principal pPrincipal) throws IOException {
|
||||||
List<ImageDTO> result = new LinkedList<>();
|
List<ImageDTO> result = new LinkedList<>();
|
||||||
|
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
result = imageRepository.getByUserId(connectedUser.get().getId())
|
result = imageRepository.getByUserId(connectedUser.get().getId())
|
||||||
.stream().map(ImageDTO::new).collect(Collectors.toList());
|
.stream().map(ImageDTO::new).collect(Collectors.toList());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.codiki.posts;
|
package org.codiki.posts;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -11,12 +12,14 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.codiki.core.entities.dto.PostDTO;
|
import org.codiki.core.entities.dto.PostDTO;
|
||||||
|
import org.codiki.core.entities.dto.View;
|
||||||
import org.codiki.core.entities.persistence.Post;
|
import org.codiki.core.entities.persistence.Post;
|
||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.repositories.PostRepository;
|
import org.codiki.core.repositories.PostRepository;
|
||||||
import org.codiki.core.security.TokenService;
|
|
||||||
import org.codiki.core.services.ParserService;
|
import org.codiki.core.services.ParserService;
|
||||||
|
import org.codiki.core.services.UserService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@@ -26,6 +29,8 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/posts")
|
@RequestMapping("/api/posts")
|
||||||
public class PostController {
|
public class PostController {
|
||||||
@@ -39,10 +44,10 @@ public class PostController {
|
|||||||
private PostRepository postRepository;
|
private PostRepository postRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private PostService postService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PostService postService;
|
private UserService userService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<PostDTO> getAll() {
|
public List<PostDTO> getAll() {
|
||||||
@@ -50,24 +55,26 @@ public class PostController {
|
|||||||
.map(PostDTO::new).collect(Collectors.toList());
|
.map(PostDTO::new).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@GetMapping("/{postKey}")
|
@GetMapping("/{postKey}")
|
||||||
public PostDTO getByKey(@PathVariable("postKey") final String pPostKey,
|
public Post getByKey(@PathVariable("postKey") final String pPostKey,
|
||||||
final HttpServletResponse response) {
|
final HttpServletResponse response) {
|
||||||
final PostDTO result = getByKeyAndSource(pPostKey, response);
|
final Post result = getByKeyAndSource(pPostKey, response);
|
||||||
if(result != null) {
|
if(result != null) {
|
||||||
result.setText(parserService.parse(result.getText()));
|
result.setText(parserService.parse(result.getText()));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@GetMapping("/{postKey}/source")
|
@GetMapping("/{postKey}/source")
|
||||||
public PostDTO getByKeyAndSource(@PathVariable("postKey")final String pPostKey,
|
public Post getByKeyAndSource(@PathVariable("postKey")final String pPostKey,
|
||||||
final HttpServletResponse response) {
|
final HttpServletResponse response) {
|
||||||
PostDTO result = null;
|
Post result = null;
|
||||||
|
|
||||||
final Optional<Post> post = postRepository.getByKey(pPostKey);
|
final Optional<Post> post = postRepository.getByKey(pPostKey);
|
||||||
if(post.isPresent()) {
|
if(post.isPresent()) {
|
||||||
result = new PostDTO(post.get());
|
result = post.get();
|
||||||
} else {
|
} else {
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
}
|
}
|
||||||
@@ -75,31 +82,35 @@ public class PostController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@GetMapping("/last")
|
@GetMapping("/last")
|
||||||
public List<PostDTO> getLast() {
|
public List<Post> getLast() {
|
||||||
return postRepository.getLast(LIMIT_POSTS_HOME).stream()
|
return postRepository.getLast(PageRequest.of(0, LIMIT_POSTS_HOME));
|
||||||
.map(PostDTO::new).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@GetMapping("/search/{searchCriteria}")
|
@GetMapping("/search/{searchCriteria}")
|
||||||
public List<PostDTO> search(@PathVariable("searchCriteria") final String pSearchCriteria) {
|
public List<Post> search(@PathVariable("searchCriteria") final String pSearchCriteria) {
|
||||||
return postService.search(pSearchCriteria);
|
return postService.search(pSearchCriteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@GetMapping("/byCategory/{categoryId}")
|
@GetMapping("/byCategory/{categoryId}")
|
||||||
public List<PostDTO> getByCategory(@PathVariable("categoryId") final Long pCategoryId) {
|
public List<Post> getByCategory(@PathVariable("categoryId") final Long pCategoryId) {
|
||||||
return postRepository.getByCategoryId(pCategoryId).stream()
|
return postRepository.getByCategoryId(pCategoryId);
|
||||||
.map(PostDTO::new).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@GetMapping("/myPosts")
|
@GetMapping("/myPosts")
|
||||||
public List<PostDTO> getMyPosts(final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
public List<Post> getMyPosts(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
|
||||||
List<PostDTO> result = new LinkedList<>();
|
final Principal pPrincipal) throws IOException {
|
||||||
|
List<Post> result = new LinkedList<>();
|
||||||
|
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
result = postRepository.getByCreator(connectedUser.get().getId())
|
// result = postRepository.getByCreator(connectedUser.get().getId())
|
||||||
.stream().map(PostDTO::new).collect(Collectors.toList());
|
// .stream().map(PostDTO::new).collect(Collectors.toList());
|
||||||
|
result = postRepository.getByCreator(connectedUser.get().getId());
|
||||||
} else {
|
} else {
|
||||||
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
@@ -107,43 +118,41 @@ public class PostController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@PostMapping("/preview")
|
@PostMapping("/preview")
|
||||||
public PostDTO preview(@RequestBody final PostDTO pPost) {
|
public Post preview(@RequestBody final Post pPost) {
|
||||||
final PostDTO result = new PostDTO();
|
pPost.setImage(pPost.getImage() == null || "".equals(pPost.getImage())
|
||||||
|
|
||||||
result.setTitle(pPost.getTitle());
|
|
||||||
result.setImage(pPost.getImage() == null || "".equals(pPost.getImage())
|
|
||||||
? "https://news-cdn.softpedia.com/images/news2/this-is-the-default-wallpaper-of-the-gnome-3-20-desktop-environment-500743-2.jpg"
|
? "https://news-cdn.softpedia.com/images/news2/this-is-the-default-wallpaper-of-the-gnome-3-20-desktop-environment-500743-2.jpg"
|
||||||
: pPost.getImage());
|
: pPost.getImage());
|
||||||
result.setDescription(pPost.getDescription());
|
pPost.setText(parserService.parse(pPost.getText()));
|
||||||
result.setText(parserService.parse(pPost.getText()));
|
|
||||||
|
|
||||||
return result;
|
return pPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonView(View.PostDTO.class)
|
||||||
@PostMapping("/")
|
@PostMapping("/")
|
||||||
public PostDTO insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
|
public Post insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) {
|
final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||||
PostDTO result = null;
|
Post result = null;
|
||||||
|
|
||||||
Optional<Post> postCreated = postService.insert(pPost, pRequest, pResponse);
|
Optional<Post> postCreated = postService.insert(pPost, pRequest, pResponse, pPrincipal);
|
||||||
|
|
||||||
if(postCreated.isPresent()) {
|
if(postCreated.isPresent()) {
|
||||||
result = new PostDTO(postCreated.get());
|
result = postCreated.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/")
|
@PutMapping("/")
|
||||||
public void update(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
|
public void update(@RequestBody final Post pPost, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
postService.update(pPost, pRequest, pResponse);
|
postService.update(pPost, pRequest, pResponse, pPrincipal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{postKey}")
|
@DeleteMapping("/{postKey}")
|
||||||
public void delete(@PathVariable("postKey") final String pPostKey,
|
public void delete(@PathVariable("postKey") final String pPostKey, final HttpServletRequest pRequest,
|
||||||
final HttpServletRequest pRequest, final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
postService.delete(pPostKey, pRequest, pResponse);
|
postService.delete(pPostKey, pRequest, pResponse, pPrincipal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.codiki.posts;
|
package org.codiki.posts;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@@ -19,7 +20,7 @@ import org.codiki.core.entities.persistence.Post;
|
|||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.repositories.CategoryRepository;
|
import org.codiki.core.repositories.CategoryRepository;
|
||||||
import org.codiki.core.repositories.PostRepository;
|
import org.codiki.core.repositories.PostRepository;
|
||||||
import org.codiki.core.security.TokenService;
|
import org.codiki.core.services.UserService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -44,13 +45,13 @@ public class PostService {
|
|||||||
private CategoryRepository categoryRepository;
|
private CategoryRepository categoryRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private UserService userService;
|
||||||
|
|
||||||
public Optional<Post> insert(final PostDTO pPost, final HttpServletRequest pRequest,
|
public Optional<Post> insert(final PostDTO pPost, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) {
|
final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||||
Optional<Post> result = Optional.empty();
|
Optional<Post> result = Optional.empty();
|
||||||
|
|
||||||
final Optional<User> user = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> user = userService.getUserByPrincipal(pPrincipal);
|
||||||
|
|
||||||
if(user.isPresent()) {
|
if(user.isPresent()) {
|
||||||
final Post postToSave = new Post(pPost);
|
final Post postToSave = new Post(pPost);
|
||||||
@@ -64,9 +65,9 @@ public class PostService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(final PostDTO pPost, final HttpServletRequest pRequest,
|
public void update(final Post pPost, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
|
|
||||||
if(connectedUser.isPresent() && connectedUser.get().getKey().equals(pPost.getAuthor().getKey())) {
|
if(connectedUser.isPresent() && connectedUser.get().getKey().equals(pPost.getAuthor().getKey())) {
|
||||||
final Optional<Post> postOpt = postRepository.getByKey(pPost.getKey());
|
final Optional<Post> postOpt = postRepository.getByKey(pPost.getKey());
|
||||||
@@ -96,10 +97,10 @@ public class PostService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void delete(final String pPostKey, final HttpServletRequest pRequest,
|
public void delete(final String pPostKey, final HttpServletRequest pRequest,
|
||||||
final HttpServletResponse pResponse) throws IOException {
|
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||||
final Optional<Post> postToDelete = postRepository.getByKey(pPostKey);
|
final Optional<Post> postToDelete = postRepository.getByKey(pPostKey);
|
||||||
if(postToDelete.isPresent()) {
|
if(postToDelete.isPresent()) {
|
||||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||||
if(connectedUser.isPresent()) {
|
if(connectedUser.isPresent()) {
|
||||||
if(connectedUser.get().getKey().equals(postToDelete.get().getAuthor().getKey())) {
|
if(connectedUser.get().getKey().equals(postToDelete.get().getAuthor().getKey())) {
|
||||||
postRepository.delete(postToDelete.get());
|
postRepository.delete(postToDelete.get());
|
||||||
@@ -114,7 +115,7 @@ public class PostService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PostDTO> search(String pSearchCriteria) {
|
public List<Post> search(String pSearchCriteria) {
|
||||||
final String[] criteriasArray = pSearchCriteria.split(" ");
|
final String[] criteriasArray = pSearchCriteria.split(" ");
|
||||||
|
|
||||||
final List<SearchEntity> listSearchEntities = new LinkedList<>();
|
final List<SearchEntity> listSearchEntities = new LinkedList<>();
|
||||||
@@ -124,7 +125,7 @@ public class PostService {
|
|||||||
});
|
});
|
||||||
Collections.sort(listSearchEntities, (e1, e2) -> e2.getScore() - e1.getScore());
|
Collections.sort(listSearchEntities, (e1, e2) -> e2.getScore() - e1.getScore());
|
||||||
|
|
||||||
return listSearchEntities.stream().map(SearchEntity::getPost).map(PostDTO::new).collect(Collectors.toList());
|
return listSearchEntities.stream().map(SearchEntity::getPost).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateScore(final SearchEntity searchPost, final String[] pCriteriasArray) {
|
void calculateScore(final SearchEntity searchPost, final String[] pCriteriasArray) {
|
||||||
|
|||||||
@@ -10,8 +10,14 @@ spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
|
|||||||
# Because detection is disabled you have to set correct dialect by hand.
|
# Because detection is disabled you have to set correct dialect by hand.
|
||||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
|
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
|
||||||
|
|
||||||
logging.level.org.hibernate=DEBUG
|
#logging.level.org.hibernate=DEBUG
|
||||||
|
|
||||||
spring.servlet.multipart.max-file-size=104857600
|
spring.servlet.multipart.max-file-size=104857600
|
||||||
|
|
||||||
|
codiki.files.upload=/opt/codiki/pictures/tmp
|
||||||
|
codiki.files.profile-images=/opt/codiki/pictures/profiles
|
||||||
|
codiki.files.images=/opt/codiki/pictures/posts
|
||||||
|
logging.level.org.codiki=DEBUG
|
||||||
|
logging.path=/opt/codiki/logs
|
||||||
|
logging.file=codiki
|
||||||
cors.enabled=false
|
cors.enabled=false
|
||||||
66
src/main/resources/logback-spring.xml
Executable file
66
src/main/resources/logback-spring.xml
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<property resource="application.properties" />
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<target>System.out</target>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>DEBUG</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
</filter>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>INFO</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
</filter>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>TRACE</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
</filter>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>WARN</level>
|
||||||
|
<onMatch>DENY</onMatch>
|
||||||
|
</filter>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>ERROR</level>
|
||||||
|
<onMatch>DENY</onMatch>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date %-5level [%-10.10thread] %-40.40logger{40} %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<target>System.err</target>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>WARN</level>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date %-5level [%-10.10thread] %-40.40logger{40} %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${logging.path}/${logging.file}.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${logging.path}/${logging.file}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||||
|
<maxFileSize>5MB</maxFileSize>
|
||||||
|
</timeBasedFileNamingAndTriggeringPolicy>
|
||||||
|
<maxHistory>30</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date %-5level [%-10.10thread] %-40.40logger{40} %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.codiki" level="INFO"/>
|
||||||
|
|
||||||
|
<root level="WARN">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
<appender-ref ref="STDERR"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<!-- <springProfile name="production">
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
</springProfile> -->
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
5
src/main/sql/update_1.1.0.sql
Executable file
5
src/main/sql/update_1.1.0.sql
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
INSERT INTO version (number) VALUES ('1.1.0');
|
||||||
|
|
||||||
|
INSERT INTO version_revision (version_id, text, bugfix) VALUES
|
||||||
|
(3, 'Migration vers Angular 7.', FALSE),
|
||||||
|
(3, 'Correction d''anomalies graphiques mineures.', TRUE);
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"project": {
|
|
||||||
"name": "codiki-bootstrap-material"
|
|
||||||
},
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"root": "src",
|
|
||||||
"outDir": "../resources/static/",
|
|
||||||
"assets": [
|
|
||||||
"assets",
|
|
||||||
"favicon.png"
|
|
||||||
],
|
|
||||||
"index": "index.html",
|
|
||||||
"main": "main.ts",
|
|
||||||
"polyfills": "polyfills.ts",
|
|
||||||
"test": "test.ts",
|
|
||||||
"tsconfig": "tsconfig.app.json",
|
|
||||||
"testTsconfig": "tsconfig.spec.json",
|
|
||||||
"prefix": "app",
|
|
||||||
"styles": [
|
|
||||||
"../node_modules/font-awesome/scss/font-awesome.scss",
|
|
||||||
"../node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
|
|
||||||
"../node_modules/angular-bootstrap-md/scss/mdb-free.scss",
|
|
||||||
"./styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": [
|
|
||||||
"../node_modules/chart.js/dist/Chart.js",
|
|
||||||
"../node_modules/hammerjs/hammer.min.js"
|
|
||||||
],
|
|
||||||
"environmentSource": "environments/environment.ts",
|
|
||||||
"environments": {
|
|
||||||
"dev": "environments/environment.ts",
|
|
||||||
"integ": "environments/environment.integ.ts",
|
|
||||||
"prod": "environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"e2e": {
|
|
||||||
"protractor": {
|
|
||||||
"config": "./protractor.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": [
|
|
||||||
{
|
|
||||||
"project": "src/tsconfig.app.json",
|
|
||||||
"exclude": "**/node_modules/**"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"project": "src/tsconfig.spec.json",
|
|
||||||
"exclude": "**/node_modules/**"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"project": "e2e/tsconfig.e2e.json",
|
|
||||||
"exclude": "**/node_modules/**"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"test": {
|
|
||||||
"karma": {
|
|
||||||
"config": "./karma.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaults": {
|
|
||||||
"styleExt": "scss",
|
|
||||||
"component": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ root = true
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = tab
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|||||||
39
src/main/ts/.gitignore
vendored
Executable file
39
src/main/ts/.gitignore
vendored
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System Files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
22
src/main/ts/.vscode/launch.json
vendored
Executable file → Normal file
22
src/main/ts/.vscode/launch.json
vendored
Executable file → Normal file
@@ -5,12 +5,28 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "ng serve",
|
"type": "firefox",
|
||||||
|
"request": "launch",
|
||||||
|
"reAttach": true,
|
||||||
|
"name": "Launch Firefox",
|
||||||
|
"url": "http://localhost:4200/",
|
||||||
|
"webRoot": "${workspaceFolder}/"
|
||||||
|
},
|
||||||
|
{
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"url": "http://localhost:4200/#",
|
"name": "Launch Chrome",
|
||||||
|
"url": "http://localhost:4200",
|
||||||
"webRoot": "${workspaceFolder}",
|
"webRoot": "${workspaceFolder}",
|
||||||
"runtimeExecutable": "/usr/bin/chromium"
|
"sourceMaps": true,
|
||||||
|
"runtimeExecutable": "/usr/bin/chromium-browser",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Firefox debugger attach",
|
||||||
|
"type": "firefox",
|
||||||
|
"request": "attach",
|
||||||
|
"url": "http://localhost:4200/",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# CodikiBootstrapMaterial
|
|
||||||
|
|
||||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.1.
|
|
||||||
|
|
||||||
## Development server
|
|
||||||
|
|
||||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
|
||||||
|
|
||||||
## Code scaffolding
|
|
||||||
|
|
||||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
||||||
|
|
||||||
## Running end-to-end tests
|
|
||||||
|
|
||||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
|
||||||
|
|
||||||
## Further help
|
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
|
||||||
157
src/main/ts/angular.json
Executable file
157
src/main/ts/angular.json
Executable file
@@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"ts-v7": {
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"projectType": "application",
|
||||||
|
"prefix": "app",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"styleext": "scss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "../resources/static/",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "src/tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss",
|
||||||
|
"node_modules/@fortawesome/fontawesome-free/scss/solid.scss",
|
||||||
|
"node_modules/@fortawesome/fontawesome-free/scss/regular.scss",
|
||||||
|
"node_modules/@fortawesome/fontawesome-free/scss/brands.scss",
|
||||||
|
"node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
|
||||||
|
"node_modules/angular-bootstrap-md/scss/mdb-free.scss",
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
"node_modules/chart.js/dist/Chart.js",
|
||||||
|
"node_modules/hammerjs/hammer.min.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"extractCss": true,
|
||||||
|
"namedChunks": false,
|
||||||
|
"aot": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true
|
||||||
|
},
|
||||||
|
"integ": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.integ.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"extractCss": true,
|
||||||
|
"namedChunks": false,
|
||||||
|
"aot": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "ts-v7:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "ts-v7:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "ts-v7:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
|
"karmaConfig": "src/karma.conf.js",
|
||||||
|
"styles": [
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"src/tsconfig.app.json",
|
||||||
|
"src/tsconfig.spec.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ts-v7-e2e": {
|
||||||
|
"root": "e2e/",
|
||||||
|
"projectType": "application",
|
||||||
|
"architect": {
|
||||||
|
"e2e": {
|
||||||
|
"builder": "@angular-devkit/build-angular:protractor",
|
||||||
|
"options": {
|
||||||
|
"protractorConfig": "e2e/protractor.conf.js",
|
||||||
|
"devServerTarget": "ts-v7:serve"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"devServerTarget": "ts-v7:serve:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultProject": "ts-v7"
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ const { SpecReporter } = require('jasmine-spec-reporter');
|
|||||||
exports.config = {
|
exports.config = {
|
||||||
allScriptsTimeout: 11000,
|
allScriptsTimeout: 11000,
|
||||||
specs: [
|
specs: [
|
||||||
'./e2e/**/*.e2e-spec.ts'
|
'./src/**/*.e2e-spec.ts'
|
||||||
],
|
],
|
||||||
capabilities: {
|
capabilities: {
|
||||||
'browserName': 'chrome'
|
'browserName': 'chrome'
|
||||||
@@ -21,7 +21,7 @@ exports.config = {
|
|||||||
},
|
},
|
||||||
onPrepare() {
|
onPrepare() {
|
||||||
require('ts-node').register({
|
require('ts-node').register({
|
||||||
project: 'e2e/tsconfig.e2e.json'
|
project: require('path').join(__dirname, './tsconfig.e2e.json')
|
||||||
});
|
});
|
||||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||||
}
|
}
|
||||||
14
src/main/ts/e2e/src/app.e2e-spec.ts
Executable file
14
src/main/ts/e2e/src/app.e2e-spec.ts
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
import { AppPage } from './app.po';
|
||||||
|
|
||||||
|
describe('workspace-project App', () => {
|
||||||
|
let page: AppPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new AppPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display welcome message', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getParagraphText()).toEqual('Welcome to ts-v7!');
|
||||||
|
});
|
||||||
|
});
|
||||||
11
src/main/ts/e2e/src/app.po.ts
Executable file
11
src/main/ts/e2e/src/app.po.ts
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
|
export class AppPage {
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
getParagraphText() {
|
||||||
|
return element(by.css('app-root h1')).getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/main/ts/e2e/tsconfig.e2e.json
Executable file
13
src/main/ts/e2e/tsconfig.e2e.json
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/app",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"jasminewd2",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
11407
src/main/ts/package-lock.json
generated
Executable file
11407
src/main/ts/package-lock.json
generated
Executable file
File diff suppressed because it is too large
Load Diff
@@ -1,53 +1,54 @@
|
|||||||
{
|
{
|
||||||
"name": "codiki-bootstrap-material",
|
"name": "ts-v7",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve --proxy-config proxy.conf.json",
|
||||||
"build": "ng build --prod",
|
"build": "ng build",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e"
|
"e2e": "ng e2e"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^5.2.0",
|
"@angular/animations": "^7.2.2",
|
||||||
"@angular/common": "^5.2.0",
|
"@angular/common": "^7.2.2",
|
||||||
"@angular/compiler": "^5.2.0",
|
"@angular/compiler": "^7.2.2",
|
||||||
"@angular/core": "^5.2.0",
|
"@angular/core": "^7.2.2",
|
||||||
"@angular/forms": "^5.2.0",
|
"@angular/forms": "^7.2.2",
|
||||||
"@angular/http": "^5.2.0",
|
"@angular/http": "^7.2.2",
|
||||||
"@angular/platform-browser": "^5.2.0",
|
"@angular/platform-browser": "^7.2.2",
|
||||||
"@angular/platform-browser-dynamic": "^5.2.0",
|
"@angular/platform-browser-dynamic": "^7.2.2",
|
||||||
"@angular/router": "^5.2.0",
|
"@angular/router": "^7.2.2",
|
||||||
"angular-bootstrap-md": "^5.2.3",
|
"@fortawesome/fontawesome-free": "^5.6.3",
|
||||||
"chart.js": "^2.5.0",
|
"@types/chart.js": "^2.7.42",
|
||||||
"core-js": "^2.4.1",
|
"angular-bootstrap-md": "^7.3.0",
|
||||||
"font-awesome": "^4.7.0",
|
"chart.js": "^2.7.3",
|
||||||
|
"core-js": "^2.5.4",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"ng2-handy-syntax-highlighter": "^1.0.12",
|
"rxjs": "~6.3.3",
|
||||||
"rxjs": "^5.5.6",
|
"tslib": "^1.9.0",
|
||||||
"zone.js": "^0.8.19"
|
"zone.js": "~0.8.26"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/cli": "~1.7.1",
|
"@angular-devkit/build-angular": "~0.12.0",
|
||||||
"@angular/compiler-cli": "^5.2.0",
|
"@angular/cli": "~7.2.3",
|
||||||
"@angular/language-service": "^5.2.0",
|
"@angular/compiler-cli": "^7.2.2",
|
||||||
"@types/jasmine": "~2.8.3",
|
"@angular/language-service": "^7.2.2",
|
||||||
"@types/jasminewd2": "~2.0.2",
|
"@types/jasmine": "^2.8.16",
|
||||||
"@types/node": "~6.0.60",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"codelyzer": "^4.0.1",
|
"@types/node": "~8.9.4",
|
||||||
"jasmine-core": "~2.8.0",
|
"codelyzer": "~4.3.0",
|
||||||
|
"jasmine-core": "~2.99.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma": "~2.0.0",
|
"karma": "~3.0.0",
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||||
"karma-jasmine": "~1.1.0",
|
"karma-jasmine": "~1.1.2",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"protractor": "~5.1.2",
|
"protractor": "~5.4.0",
|
||||||
"ts-node": "~4.1.0",
|
"ts-node": "~7.0.0",
|
||||||
"tslint": "~5.9.1",
|
"tslint": "~5.11.0",
|
||||||
"typescript": "~2.5.3"
|
"typescript": "~3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/main/ts/proxy.conf.json
Executable file
6
src/main/ts/proxy.conf.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"/api": {
|
||||||
|
"target": "http://localhost:8080",
|
||||||
|
"secure": false
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/main/ts/src/app/account-settings/account-settings.component.html
Executable file → Normal file
0
src/main/ts/src/app/account-settings/account-settings.component.html
Executable file → Normal file
0
src/main/ts/src/app/account-settings/account-settings.component.ts
Executable file → Normal file
0
src/main/ts/src/app/account-settings/account-settings.component.ts
Executable file → Normal file
15
src/main/ts/src/app/account-settings/change-password/change-password.component.html
Executable file → Normal file
15
src/main/ts/src/app/account-settings/change-password/change-password.component.html
Executable file → Normal file
@@ -3,7 +3,7 @@
|
|||||||
<h4 class="card-title">Mot de passe</h4>
|
<h4 class="card-title">Mot de passe</h4>
|
||||||
<form (ngSubmit)="onSubmit()" #changePasswordForm="ngForm">
|
<form (ngSubmit)="onSubmit()" #changePasswordForm="ngForm">
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="oldPassword"
|
id="oldPassword"
|
||||||
name="oldPassword"
|
name="oldPassword"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -11,12 +11,12 @@
|
|||||||
[(ngModel)]="model.oldPassword"
|
[(ngModel)]="model.oldPassword"
|
||||||
#oldPassword="ngModel"
|
#oldPassword="ngModel"
|
||||||
data-error="Veuillez saisir votre mot de passe actuel"
|
data-error="Veuillez saisir votre mot de passe actuel"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="oldPassword">Mot de passe actuel</label>
|
<label for="oldPassword">Mot de passe actuel</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="newPassword"
|
id="newPassword"
|
||||||
name="newPassword"
|
name="newPassword"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -24,12 +24,12 @@
|
|||||||
[(ngModel)]="model.newPassword"
|
[(ngModel)]="model.newPassword"
|
||||||
#newPassword="ngModel"
|
#newPassword="ngModel"
|
||||||
data-error="Veuillez saisir votre nouveau mot de passe"
|
data-error="Veuillez saisir votre nouveau mot de passe"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="newPassword">Nouveau mot de passe</label>
|
<label for="newPassword">Nouveau mot de passe</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -37,11 +37,11 @@
|
|||||||
[(ngModel)]="model.confirmPassword"
|
[(ngModel)]="model.confirmPassword"
|
||||||
#confirmPassword="ngModel"
|
#confirmPassword="ngModel"
|
||||||
data-error="Veuillez confirmer votre nouveau mot de passe"
|
data-error="Veuillez confirmer votre nouveau mot de passe"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="confirmPassword">Confirmation de votre mot de passe</label>
|
<label for="confirmPassword">Confirmation de votre mot de passe</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="card red lighten-2 text-center z-depth-2" *ngIf="error">
|
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="white-text mb-0">{{error}}</p>
|
<p class="white-text mb-0">{{error}}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,4 +58,3 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
33
src/main/ts/src/app/account-settings/change-password/change-password.component.ts
Executable file → Normal file
33
src/main/ts/src/app/account-settings/change-password/change-password.component.ts
Executable file → Normal file
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { PasswordWrapper } from '../../core/entities';
|
import { PasswordWrapper } from '../../core/entities';
|
||||||
import { ChangePasswordService } from './change-password.service';
|
import { ChangePasswordService } from './change-password.service';
|
||||||
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
import { RouteReuseStrategy, Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-change-password',
|
selector: 'app-change-password',
|
||||||
@@ -14,24 +14,47 @@ import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
|||||||
.submitFormArea {
|
.submitFormArea {
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#errorMsg {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.5s ease-out;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
`]
|
`]
|
||||||
})
|
})
|
||||||
export class ChangePasswordComponent {
|
export class ChangePasswordComponent {
|
||||||
model: PasswordWrapper = new PasswordWrapper('', '', '');
|
model: PasswordWrapper = new PasswordWrapper('', '', '');
|
||||||
|
|
||||||
error: string;
|
error: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private router: Router,
|
||||||
private changePasswordService: ChangePasswordService
|
private changePasswordService: ChangePasswordService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onSubmit(): void {
|
onSubmit(): void {
|
||||||
if (this.model.newPassword !== this.model.confirmPassword) {
|
if (this.model.newPassword !== this.model.confirmPassword) {
|
||||||
this.error = 'Les mots de passe saisis ne correspondent pas.';
|
this.setMessage('Les mots de passe saisis ne correspondent pas.');
|
||||||
} else {
|
} else {
|
||||||
this.changePasswordService.changePassword(this.model).subscribe(null, error => {
|
this.changePasswordService.changePassword(this.model).subscribe(() => {
|
||||||
this.error = 'Le mot de passe saisi ne correspond pas au votre.';
|
this.router.navigate(['/accountSettings']);
|
||||||
|
}, error => {
|
||||||
|
this.setMessage('Le mot de passe saisi ne correspond pas au votre.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMessage(message: string): void {
|
||||||
|
this.error = message;
|
||||||
|
|
||||||
|
const resultMsgDiv = document.getElementById('errorMsg');
|
||||||
|
resultMsgDiv.style.maxHeight = '64px';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resultMsgDiv.style.maxHeight = '0px';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.error = undefined;
|
||||||
|
}, 550);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/main/ts/src/app/account-settings/change-password/change-password.service.ts
Executable file → Normal file
8
src/main/ts/src/app/account-settings/change-password/change-password.service.ts
Executable file → Normal file
@@ -1,10 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
import { PasswordWrapper } from '../../core/entities';
|
import { PasswordWrapper } from '../../core/entities';
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
|
|
||||||
const ACCOUNT_URL = environment.apiUrl + '/api/account';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ChangePasswordService {
|
export class ChangePasswordService {
|
||||||
@@ -12,6 +10,6 @@ export class ChangePasswordService {
|
|||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
changePassword(passwordWrapper: PasswordWrapper): Observable<any> {
|
changePassword(passwordWrapper: PasswordWrapper): Observable<any> {
|
||||||
return this.http.put(ACCOUNT_URL + '/changePassword', passwordWrapper);
|
return this.http.put('/api/account/changePassword', passwordWrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/main/ts/src/app/account-settings/profil-edition/profil-edition.component.html
Executable file → Normal file
16
src/main/ts/src/app/account-settings/profil-edition/profil-edition.component.html
Executable file → Normal file
@@ -1,16 +1,16 @@
|
|||||||
<input id="profilImageInput" type="file" (change)="uploadImage($event)" [hidden]="true">
|
<input id="profilImageInput" type="file" (change)="uploadImage($event)" [hidden]="true">
|
||||||
<div class="col-md-8 offset-md-2 col-lg-6 offset-lg-3">
|
<div class="col-md-8 offset-md-2 col-lg-6 offset-lg-3">
|
||||||
<div class="card">
|
<mdb-card>
|
||||||
<img id="profil-image" class="card-img-top"
|
<img id="profil-image" class="card-img-top"
|
||||||
(click)="triggerProfilImageChange()"
|
(click)="triggerProfilImageChange()"
|
||||||
[src]="getAvatarUrl()"
|
[src]="getAvatarUrl()"
|
||||||
alt="Card image cap"
|
alt="Card image cap"
|
||||||
mdbTooltip="Modifier mon image de profil"
|
mdbTooltip="Modifier mon image de profil"
|
||||||
placement="bottom">
|
placement="bottom">
|
||||||
<div id="form" class="card-body">
|
<mdb-card-body id="form">
|
||||||
<form (ngSubmit)="onSubmit()" #profilEditionForm="ngForm">
|
<form (ngSubmit)="onSubmit()" #profilEditionForm="ngForm">
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="name"
|
id="name"
|
||||||
name="name"
|
name="name"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -18,12 +18,12 @@
|
|||||||
[(ngModel)]="model.name"
|
[(ngModel)]="model.name"
|
||||||
#name="ngModel"
|
#name="ngModel"
|
||||||
data-error="Veuillez saisir votre nom d'utilisateur"
|
data-error="Veuillez saisir votre nom d'utilisateur"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="name">Nom d'utilisateur</label>
|
<label for="name">Nom d'utilisateur</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
[(ngModel)]="model.email"
|
[(ngModel)]="model.email"
|
||||||
#email="ngModel"
|
#email="ngModel"
|
||||||
data-error="Veuillez saisir votre adresse email"
|
data-error="Veuillez saisir votre adresse email"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="email">Email</label>
|
<label for="email">Email</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,6 +55,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</mdb-card-body>
|
||||||
</div>
|
</mdb-card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
7
src/main/ts/src/app/account-settings/profil-edition/profil-edition.component.ts
Executable file → Normal file
7
src/main/ts/src/app/account-settings/profil-edition/profil-edition.component.ts
Executable file → Normal file
@@ -3,8 +3,6 @@ import { ProfilEditionService } from './profil-edition.service';
|
|||||||
import { HttpEventType, HttpResponse } from '@angular/common/http';
|
import { HttpEventType, HttpResponse } from '@angular/common/http';
|
||||||
import { User } from '../../core/entities';
|
import { User } from '../../core/entities';
|
||||||
import { AuthService } from '../../core/services/auth.service';
|
import { AuthService } from '../../core/services/auth.service';
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profil-edition',
|
selector: 'app-profil-edition',
|
||||||
@@ -49,7 +47,7 @@ export class ProfilEditionComponent implements OnInit {
|
|||||||
|
|
||||||
getAvatarUrl(): string {
|
getAvatarUrl(): string {
|
||||||
return this.model.image
|
return this.model.image
|
||||||
? `${environment.apiUrl}/api/images/loadAvatar/${this.model.image}`
|
? `/api/images/loadAvatar/${this.model.image}`
|
||||||
: './assets/images/default_user.png';
|
: './assets/images/default_user.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +68,8 @@ export class ProfilEditionComponent implements OnInit {
|
|||||||
} else if (result instanceof HttpResponse) {
|
} else if (result instanceof HttpResponse) {
|
||||||
console.log('File ' + result.body + ' completely uploaded!');
|
console.log('File ' + result.body + ' completely uploaded!');
|
||||||
this.model.image = result.body as string;
|
this.model.image = result.body as string;
|
||||||
this.authService.setUser(this.model);
|
this.authService.setAuthenticated(this.model);
|
||||||
|
this.setMessage('Image de profil modifiée.', false);
|
||||||
}
|
}
|
||||||
this.selectedFiles = undefined;
|
this.selectedFiles = undefined;
|
||||||
});
|
});
|
||||||
|
|||||||
10
src/main/ts/src/app/account-settings/profil-edition/profil-edition.service.ts
Executable file → Normal file
10
src/main/ts/src/app/account-settings/profil-edition/profil-edition.service.ts
Executable file → Normal file
@@ -1,12 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {HttpClient, HttpRequest, HttpEvent} from '@angular/common/http';
|
import {HttpClient, HttpRequest, HttpEvent} from '@angular/common/http';
|
||||||
import {Observable} from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
import { User } from '../../core/entities';
|
import { User } from '../../core/entities';
|
||||||
|
|
||||||
const IMAGES_URL = environment.apiUrl + '/api/images';
|
|
||||||
const ACCOUNT_URL = environment.apiUrl + '/api/account';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProfilEditionService {
|
export class ProfilEditionService {
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
@@ -17,7 +13,7 @@ export class ProfilEditionService {
|
|||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
return this.http.request(new HttpRequest(
|
return this.http.request(new HttpRequest(
|
||||||
'POST', IMAGES_URL + '/uploadAvatar', formData, {
|
'POST', '/api/images/uploadAvatar', formData, {
|
||||||
reportProgress: true,
|
reportProgress: true,
|
||||||
responseType: 'text'
|
responseType: 'text'
|
||||||
}
|
}
|
||||||
@@ -25,6 +21,6 @@ export class ProfilEditionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateUser(user: User): Observable<any> {
|
updateUser(user: User): Observable<any> {
|
||||||
return this.http.put<any>(`${ACCOUNT_URL}/`, user);
|
return this.http.put<any>(`/api/account/`, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
main {
|
|
||||||
flex:1 0 auto;
|
|
||||||
}
|
|
||||||
@@ -13,15 +13,15 @@ describe('AppComponent', () => {
|
|||||||
const app = fixture.debugElement.componentInstance;
|
const app = fixture.debugElement.componentInstance;
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
it(`should have as title 'app'`, async(() => {
|
it(`should have as title 'ts-v7'`, async(() => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.debugElement.componentInstance;
|
const app = fixture.debugElement.componentInstance;
|
||||||
expect(app.title).toEqual('app');
|
expect(app.title).toEqual('ts-v7');
|
||||||
}));
|
}));
|
||||||
it('should render title in a h1 tag', async(() => {
|
it('should render title in a h1 tag', async(() => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const compiled = fixture.debugElement.nativeElement;
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
|
expect(compiled.querySelector('h1').textContent).toContain('Welcome to ts-v7!');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ import { Component } from '@angular/core';
|
|||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'app';
|
title = 'codiki';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
// Angular core
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { RouterModule } from '@angular/router';
|
import { HttpModule } from '@angular/http';
|
||||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { MDBBootstrapModule } from 'angular-bootstrap-md';
|
import { MDBBootstrapModule } from 'angular-bootstrap-md';
|
||||||
|
|
||||||
// Router
|
// Router
|
||||||
import { appRoutes } from './app.routes';
|
import { appRoutes } from './app.routing';
|
||||||
|
|
||||||
|
// Guard
|
||||||
|
import { AuthGuard } from './core/guards/auth.guard';
|
||||||
|
|
||||||
|
// Interceptor
|
||||||
|
import { UnauthorizedInterceptor } from './core/interceptors/unauthorized.interceptor';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
@@ -19,7 +25,6 @@ import { NotFoundComponent } from './not-found/not-found.component';
|
|||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
import { LoginComponent } from './login/login.component';
|
import { LoginComponent } from './login/login.component';
|
||||||
import { DisconnectionComponent } from './disconnection/disconnection.component';
|
import { DisconnectionComponent } from './disconnection/disconnection.component';
|
||||||
import { PostCardComponent } from './core/post-card/post-card.component';
|
|
||||||
import { PostComponent } from './posts/post.component';
|
import { PostComponent } from './posts/post.component';
|
||||||
import { ByCategoryComponent } from './posts/byCategory/by-category.component';
|
import { ByCategoryComponent } from './posts/byCategory/by-category.component';
|
||||||
import { MyPostsComponent } from './posts/myPosts/my-posts.component';
|
import { MyPostsComponent } from './posts/myPosts/my-posts.component';
|
||||||
@@ -32,10 +37,11 @@ import { SearchComponent } from './search/search.component';
|
|||||||
import { SigninComponent } from './signin/signin.component';
|
import { SigninComponent } from './signin/signin.component';
|
||||||
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
||||||
|
|
||||||
// html components
|
// Reusable components
|
||||||
import { ProgressBarComponent } from './core/directives/progress-bar/progress-bar.component';
|
import { PostCardComponent } from './core/post-card/post-card.component';
|
||||||
import { SpinnerComponent } from './core/directives/spinner/spinner.component';
|
import { SpinnerComponent } from './core/directives/spinner/spinner.component';
|
||||||
import { SearchBarComponent } from './core/directives/search-bar/search-bar.component';
|
import { SearchBarComponent } from './core/directives/search-bar/search-bar.component';
|
||||||
|
import { ProgressBarComponent } from './core/directives/progress-bar/progress-bar.component';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { AuthService } from './core/services/auth.service';
|
import { AuthService } from './core/services/auth.service';
|
||||||
@@ -49,73 +55,63 @@ import { ProfilEditionService } from './account-settings/profil-edition/profil-e
|
|||||||
import { HeaderService } from './header/header.service';
|
import { HeaderService } from './header/header.service';
|
||||||
import { CreateUpdatePostService } from './posts/create-update/create-update-post.service';
|
import { CreateUpdatePostService } from './posts/create-update/create-update-post.service';
|
||||||
import { SearchService } from './search/search.service';
|
import { SearchService } from './search/search.service';
|
||||||
import { VersionRevisionService } from './version-revisions/version-revisions.service';
|
|
||||||
|
|
||||||
// Guards
|
|
||||||
import { AuthGuard } from './core/guards/auth.guard';
|
|
||||||
|
|
||||||
// Interceptors
|
|
||||||
import { TokenInterceptor } from './core/interceptors/token-interceptor';
|
|
||||||
import { SigninService } from './signin/signin.service';
|
import { SigninService } from './signin/signin.service';
|
||||||
|
import { VersionRevisionService } from './version-revisions/version-revisions.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
NotFoundComponent,
|
|
||||||
HomeComponent,
|
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
|
SigninComponent,
|
||||||
DisconnectionComponent,
|
DisconnectionComponent,
|
||||||
|
HomeComponent,
|
||||||
PostCardComponent,
|
PostCardComponent,
|
||||||
PostComponent,
|
SpinnerComponent,
|
||||||
ByCategoryComponent,
|
|
||||||
MyPostsComponent,
|
MyPostsComponent,
|
||||||
AccountSettingsComponent,
|
AccountSettingsComponent,
|
||||||
CreateUpdatePostComponent,
|
|
||||||
ChangePasswordComponent,
|
ChangePasswordComponent,
|
||||||
ProfilEditionComponent,
|
ProfilEditionComponent,
|
||||||
ForbiddenComponent,
|
PostComponent,
|
||||||
|
NotFoundComponent,
|
||||||
|
ByCategoryComponent,
|
||||||
|
CreateUpdatePostComponent,
|
||||||
|
VersionRevisionComponent,
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
SigninComponent,
|
|
||||||
ProgressBarComponent,
|
|
||||||
SpinnerComponent,
|
|
||||||
SearchBarComponent,
|
SearchBarComponent,
|
||||||
VersionRevisionComponent
|
ProgressBarComponent,
|
||||||
|
ForbiddenComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
HttpClientModule,
|
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
HttpModule,
|
||||||
|
HttpClientModule,
|
||||||
MDBBootstrapModule.forRoot(),
|
MDBBootstrapModule.forRoot(),
|
||||||
RouterModule.forRoot(
|
RouterModule.forRoot(
|
||||||
appRoutes,
|
appRoutes,
|
||||||
// { enableTracing: true } // Enabling tracing
|
// { enableTracing: true }, // Enabling tracing
|
||||||
{ onSameUrlNavigation: 'reload' }
|
{ onSameUrlNavigation: 'reload'}
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
AuthGuard,
|
||||||
|
HeaderService,
|
||||||
AuthService,
|
AuthService,
|
||||||
|
HomeService,
|
||||||
LoginService,
|
LoginService,
|
||||||
SigninService,
|
SigninService,
|
||||||
HomeService,
|
|
||||||
PostService,
|
|
||||||
ByCategoryService,
|
|
||||||
MyPostsService,
|
MyPostsService,
|
||||||
ChangePasswordService,
|
ChangePasswordService,
|
||||||
ProfilEditionService,
|
ProfilEditionService,
|
||||||
HeaderService,
|
PostService,
|
||||||
|
ByCategoryService,
|
||||||
CreateUpdatePostService,
|
CreateUpdatePostService,
|
||||||
SearchService,
|
|
||||||
VersionRevisionService,
|
VersionRevisionService,
|
||||||
AuthGuard,
|
SearchService,
|
||||||
{
|
{ provide: HTTP_INTERCEPTORS, useClass: UnauthorizedInterceptor, multi: true }
|
||||||
provide: HTTP_INTERCEPTORS,
|
|
||||||
useClass: TokenInterceptor,
|
|
||||||
multi: true
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent]
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
import { HomeComponent } from './home/home.component';
|
import { AuthGuard } from './core/guards/auth.guard';
|
||||||
import { NotFoundComponent } from './not-found/not-found.component';
|
|
||||||
import { LoginComponent } from './login/login.component';
|
import { LoginComponent } from './login/login.component';
|
||||||
import { SigninComponent } from './signin/signin.component';
|
import { SigninComponent } from './signin/signin.component';
|
||||||
|
import { HomeComponent } from './home/home.component';
|
||||||
import { DisconnectionComponent } from './disconnection/disconnection.component';
|
import { DisconnectionComponent } from './disconnection/disconnection.component';
|
||||||
import { PostComponent } from './posts/post.component';
|
|
||||||
import { ByCategoryComponent } from './posts/byCategory/by-category.component';
|
|
||||||
import { MyPostsComponent } from './posts/myPosts/my-posts.component';
|
import { MyPostsComponent } from './posts/myPosts/my-posts.component';
|
||||||
import { AccountSettingsComponent } from './account-settings/account-settings.component';
|
import { AccountSettingsComponent } from './account-settings/account-settings.component';
|
||||||
import { ChangePasswordComponent } from './account-settings/change-password/change-password.component';
|
import { ChangePasswordComponent } from './account-settings/change-password/change-password.component';
|
||||||
import { CreateUpdatePostComponent } from './posts/create-update/create-update-post.component';
|
|
||||||
import { ForbiddenComponent } from './forbidden/forbidden.component';
|
|
||||||
import { ProfilEditionComponent } from './account-settings/profil-edition/profil-edition.component';
|
import { ProfilEditionComponent } from './account-settings/profil-edition/profil-edition.component';
|
||||||
import { SearchComponent } from './search/search.component';
|
import { PostComponent } from './posts/post.component';
|
||||||
|
import { ByCategoryComponent } from './posts/byCategory/by-category.component';
|
||||||
|
import { CreateUpdatePostComponent } from './posts/create-update/create-update-post.component';
|
||||||
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
||||||
|
import { SearchComponent } from './search/search.component';
|
||||||
import { AuthGuard } from './core/guards/auth.guard';
|
|
||||||
|
|
||||||
export const appRoutes: Routes = [
|
export const appRoutes: Routes = [
|
||||||
{ path: 'login', component: LoginComponent },
|
{ path: 'login', component: LoginComponent },
|
||||||
@@ -27,13 +25,12 @@ export const appRoutes: Routes = [
|
|||||||
{ path: 'posts/update/:postKey', component: CreateUpdatePostComponent, canActivate: [AuthGuard] },
|
{ path: 'posts/update/:postKey', component: CreateUpdatePostComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'posts/:postKey', component: PostComponent },
|
{ path: 'posts/:postKey', component: PostComponent },
|
||||||
{ path: 'posts/byCategory/:categoryId', component: ByCategoryComponent},
|
{ path: 'posts/byCategory/:categoryId', component: ByCategoryComponent},
|
||||||
{ path: 'posts/search/:searchCriteria', component: SearchComponent},
|
{ path: 'posts/search/:searchCriteria', component: SearchComponent },
|
||||||
{ path: 'myPosts', component: MyPostsComponent, canActivate: [AuthGuard]},
|
{ path: 'myPosts', component: MyPostsComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'accountSettings', component: AccountSettingsComponent, canActivate: [AuthGuard] },
|
{ path: 'accountSettings', component: AccountSettingsComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'changePassword', component: ChangePasswordComponent, canActivate: [AuthGuard] },
|
{ path: 'changePassword', component: ChangePasswordComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'profilEdit', component: ProfilEditionComponent, canActivate: [AuthGuard] },
|
{ path: 'profilEdit', component: ProfilEditionComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'versionrevisions', component: VersionRevisionComponent },
|
{ path: 'versionrevisions', component: VersionRevisionComponent },
|
||||||
{ path: 'forbidden', component: ForbiddenComponent },
|
|
||||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||||
{ path: '**', component: NotFoundComponent }
|
{ path: '**', redirectTo: '/home' }
|
||||||
];
|
];
|
||||||
@@ -27,8 +27,10 @@ import { Router } from '@angular/router';
|
|||||||
color: white;
|
color: white;
|
||||||
background-color: #5c6bc0;
|
background-color: #5c6bc0;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
border-style: unset;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 35px;
|
padding-right: 35px;
|
||||||
|
transition: all 0.2s ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
input#search:focus {
|
input#search:focus {
|
||||||
|
|||||||
0
src/main/ts/src/app/core/guards/auth.guard.ts
Executable file → Normal file
0
src/main/ts/src/app/core/guards/auth.guard.ts
Executable file → Normal file
@@ -1,43 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import 'rxjs/add/operator/do';
|
|
||||||
|
|
||||||
import { User } from '../entities';
|
|
||||||
import { AuthService } from '../services/auth.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TokenInterceptor implements HttpInterceptor {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private authService: AuthService,
|
|
||||||
private router: Router
|
|
||||||
) {}
|
|
||||||
|
|
||||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
|
||||||
const token = this.authService.getToken();
|
|
||||||
|
|
||||||
let request: HttpRequest<any> = req;
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
request = req.clone({
|
|
||||||
setHeaders: {
|
|
||||||
token: token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return next.handle(request).do((event: HttpEvent<any>) => {
|
|
||||||
// Do nothing for the interceptor
|
|
||||||
}, (err: any) => {
|
|
||||||
if (err instanceof HttpErrorResponse && err.status === 401) {
|
|
||||||
this.authService.disconnect();
|
|
||||||
this.router.navigate(['/login']);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
|
||||||
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private authService: AuthService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
intercept(pRequest: HttpRequest<any>, pNext: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
return pNext.handle(pRequest).pipe(catchError(err => {
|
||||||
|
if (err.status === 401) {
|
||||||
|
this.authService.setAnonymous();
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = err.error.message || err.statusText;
|
||||||
|
return throwError(error);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { Post } from '../entities';
|
import { Post } from '../entities';
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-post-card',
|
selector: 'app-post-card',
|
||||||
@@ -54,7 +53,7 @@ export class PostCardComponent {
|
|||||||
|
|
||||||
getAvatarUrl(): string {
|
getAvatarUrl(): string {
|
||||||
return this.post.author.image
|
return this.post.author.image
|
||||||
? `${environment.apiUrl}/api/images/loadAvatar/${this.post.author.image}`
|
? `/api/images/loadAvatar/${this.post.author.image}`
|
||||||
: './assets/images/default_user.png';
|
: './assets/images/default_user.png';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,28 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { User } from '../entities';
|
import { User } from '../entities';
|
||||||
|
|
||||||
const PARAM_TOKEN = 'token';
|
|
||||||
const PARAM_USER = 'user';
|
const PARAM_USER = 'user';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
public getToken(): string {
|
public setAuthenticated(user: User): void {
|
||||||
return localStorage.getItem(PARAM_TOKEN);
|
this.setUser(user);
|
||||||
}
|
}
|
||||||
|
public setAnonymous(): void {
|
||||||
public setToken(token: string): void {
|
localStorage.clear();
|
||||||
localStorage.setItem(PARAM_TOKEN, token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isAuthenticated(): boolean {
|
public isAuthenticated(): boolean {
|
||||||
return this.getToken() != null;
|
return this.getUser() != null;
|
||||||
}
|
|
||||||
|
|
||||||
public disconnect(): void {
|
|
||||||
localStorage.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isAdmin(): boolean {
|
public isAdmin(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setUser(user: User): void {
|
private setUser(user: User): void {
|
||||||
localStorage.setItem(PARAM_USER, JSON.stringify(user));
|
localStorage.setItem(PARAM_USER, JSON.stringify(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { Http } from '@angular/http';
|
||||||
|
|
||||||
import { AuthService } from '../core/services/auth.service';
|
import { AuthService } from '../core/services/auth.service';
|
||||||
|
|
||||||
@@ -11,11 +12,18 @@ export class DisconnectionComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private router: Router
|
private router: Router,
|
||||||
|
private http: Http
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.authService.disconnect();
|
this.http.get('/api/account/logout').subscribe(() => {
|
||||||
|
console.log('Logout success.');
|
||||||
|
this.authService.setAnonymous();
|
||||||
|
}, error => {
|
||||||
|
console.error('Error during logout from API.', error);
|
||||||
|
}, () => {
|
||||||
this.router.navigate(['/home']);
|
this.router.navigate(['/home']);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/main/ts/src/app/footer/footer.component.html
Executable file → Normal file
2
src/main/ts/src/app/footer/footer.component.html
Executable file → Normal file
@@ -3,7 +3,7 @@
|
|||||||
<div class="footer-copyright">
|
<div class="footer-copyright">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="float-left">
|
<span class="float-left">
|
||||||
<span class="anticopy">©</span> 2017 Tous droits réservés -
|
<span class="anticopy">©</span> 2017 - 2019 Tous droits réservés -
|
||||||
<i id="appVersion" routerLink="/versionrevisions" mdbTooltip="Notes de versions" placement="top" mdbRippleRadius>
|
<i id="appVersion" routerLink="/versionrevisions" mdbTooltip="Notes de versions" placement="top" mdbRippleRadius>
|
||||||
{{appVersion}}
|
{{appVersion}}
|
||||||
</i>
|
</i>
|
||||||
|
|||||||
6
src/main/ts/src/app/footer/footer.component.scss
Executable file → Normal file
6
src/main/ts/src/app/footer/footer.component.scss
Executable file → Normal file
@@ -5,6 +5,10 @@ footer {
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-copyright {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.page-footer {
|
.page-footer {
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
}
|
}
|
||||||
@@ -16,4 +20,6 @@ span.anticopy {
|
|||||||
|
|
||||||
#appVersion {
|
#appVersion {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
display: inline;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
0
src/main/ts/src/app/footer/footer.component.ts
Executable file → Normal file
0
src/main/ts/src/app/footer/footer.component.ts
Executable file → Normal file
0
src/main/ts/src/app/forbidden/forbidden.component.ts
Executable file → Normal file
0
src/main/ts/src/app/forbidden/forbidden.component.ts
Executable file → Normal file
@@ -1,4 +1,4 @@
|
|||||||
<mdb-navbar SideClass="navbar fixed-top navbar-expand-lg navbar-dark bg-indigo scrolling-navbar ie-nav" [containerInside]="false">
|
<mdb-navbar SideClass="navbar fixed-top navbar-expand-lg navbar-dark indigo scrolling-navbar ie-nav" [containerInside]="false">
|
||||||
<logo>
|
<logo>
|
||||||
<a class="logo navbar-brand" routerLink="/">
|
<a class="logo navbar-brand" routerLink="/">
|
||||||
<img id="logo" src="./assets/images/codiki.png" alt="logo" />
|
<img id="logo" src="./assets/images/codiki.png" alt="logo" />
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
<i class="fa fa-list-alt"></i> Mes articles
|
<i class="fa fa-list-alt"></i> Mes articles
|
||||||
</a>
|
</a>
|
||||||
<a class="dropdown-item waves-light" mdbRippleRadius routerLink="/accountSettings">
|
<a class="dropdown-item waves-light" mdbRippleRadius routerLink="/accountSettings">
|
||||||
<i class="fa fa-gear"></i> Paramètres
|
<i class="fa fa-cog"></i> Paramètres
|
||||||
</a>
|
</a>
|
||||||
<a class="dropdown-item waves-light danger-color-dark" mdbRippleRadius routerLink="/disconnection">
|
<a class="dropdown-item waves-light danger-color-dark" mdbRippleRadius routerLink="/disconnection">
|
||||||
<span class="white-text">
|
<span class="white-text">
|
||||||
<i class="fa fa-sign-out"></i> Déconnexion
|
<i class="fa fa-sign-out-alt"></i> Déconnexion
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<div *ngFor="let category of listCategories">
|
<div *ngFor="let category of listCategories">
|
||||||
<a [id]="'category-' + category.id" class="collapsible" *ngIf="category.listSubCategories.length" (click)="openCategoriesLinks(category)">
|
<a [id]="'category-' + category.id" class="collapsible" *ngIf="category.listSubCategories.length" (click)="openCategoriesLinks(category)">
|
||||||
{{category.name}} <i class="fa fa-chevron-down pull-right"></i>
|
{{category.name}} <i class="fa {{accordionOpenned[category.id] ? 'fa-chevron-down' : 'fa-chevron-right'}} float-right"></i>
|
||||||
</a>
|
</a>
|
||||||
<div class="categoriesLinks" >
|
<div class="categoriesLinks" >
|
||||||
<a *ngFor="let subCategory of category.listSubCategories"
|
<a *ngFor="let subCategory of category.listSubCategories"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
height: 100%; /* 100% Full-height */
|
height: 100%; /* 100% Full-height */
|
||||||
width: 0; /* 0 width - change this with JavaScript */
|
width: 0; /* 0 width - change this with JavaScript */
|
||||||
position: fixed; /* Stay in place */
|
position: fixed; /* Stay in place */
|
||||||
z-index: 1000; /* Stay on top */
|
z-index: 1050; /* Stay on top */
|
||||||
top: 0; /* Stay at the top */
|
top: 0; /* Stay at the top */
|
||||||
left: 0;
|
left: 0;
|
||||||
background-color: #3f51b5;
|
background-color: #3f51b5;
|
||||||
@@ -85,3 +85,11 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: max-height 0.2s ease-out;
|
transition: max-height 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-item.danger-color-dark:hover {
|
||||||
|
background-color: #ff4444 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible i.fa {
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export class HeaderComponent implements OnInit {
|
|||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
listCategories: Array<Category> = [];
|
listCategories: Array<Category> = [];
|
||||||
title: string;
|
title: string;
|
||||||
|
accordionOpenned: Array<Boolean> = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
@@ -73,8 +74,10 @@ export class HeaderComponent implements OnInit {
|
|||||||
const divContent = divCategoriesLinks.nextElementSibling as HTMLElement;
|
const divContent = divCategoriesLinks.nextElementSibling as HTMLElement;
|
||||||
if (divContent.style.maxHeight) {
|
if (divContent.style.maxHeight) {
|
||||||
divContent.style.maxHeight = null;
|
divContent.style.maxHeight = null;
|
||||||
|
this.accordionOpenned[category.id] = false;
|
||||||
} else {
|
} else {
|
||||||
divContent.style.maxHeight = divContent.scrollHeight + 'px';
|
divContent.style.maxHeight = divContent.scrollHeight + 'px';
|
||||||
|
this.accordionOpenned[category.id] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
import { Category } from '../core/entities';
|
import { Category } from '../core/entities';
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
const CATEGORY_URL = environment.apiUrl + '/api/categories/';
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HeaderService {
|
export class HeaderService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(
|
||||||
|
private http: HttpClient
|
||||||
|
) {}
|
||||||
|
|
||||||
getAllCategories(): Observable<Array<Category>> {
|
getAllCategories(): Observable<Array<Category>> {
|
||||||
return this.http.get<Array<Category>>(CATEGORY_URL);
|
return this.http.get<Array<Category>>('/api/categories/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
import { Post } from '../core/entities';
|
import { Post } from '../core/entities';
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
const POSTS_URL = environment.apiUrl + '/api/posts';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HomeService {
|
export class HomeService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
getLastPosts(): Observable<Array<Post>> {
|
getLastPosts(): Observable<Array<Post>> {
|
||||||
return this.http.get<Array<Post>>(POSTS_URL + '/last');
|
return this.http.get<Array<Post>>('/api/posts/last');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<form id="form" (ngSubmit)="onSubmit()" #loginForm="ngForm">
|
<form id="form" (ngSubmit)="onSubmit()" #loginForm="ngForm">
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<i class="fa fa-envelope prefix grey-text"></i>
|
<i class="fa fa-envelope prefix grey-text"></i>
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
@@ -12,13 +12,13 @@
|
|||||||
[(ngModel)]="model.email"
|
[(ngModel)]="model.email"
|
||||||
#email="ngModel"
|
#email="ngModel"
|
||||||
data-error="Veuillez saisir une adresse email valide"
|
data-error="Veuillez saisir une adresse email valide"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="email">Email</label>
|
<label for="email">Email</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<i class="fa fa-lock prefix grey-text"></i>
|
<i class="fa fa-lock prefix grey-text"></i>
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
[(ngModel)]="model.password"
|
[(ngModel)]="model.password"
|
||||||
#password="ngModel"
|
#password="ngModel"
|
||||||
data-error="Veuillez saisir votre mot de passe"
|
data-error="Veuillez saisir votre mot de passe"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="password">Password</label>
|
<label for="password">Mot de passe</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { User } from '../core/entities';
|
|
||||||
import { AuthService } from '../core/services/auth.service';
|
|
||||||
import { LoginService } from './login.service';
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { User } from '../core/entities';
|
||||||
|
import { LoginService } from './login.service';
|
||||||
|
import { AuthService } from '../core/services/auth.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: 'app-login',
|
||||||
@@ -29,20 +29,24 @@ export class LoginComponent {
|
|||||||
loginError: string;
|
loginError: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private router: Router,
|
||||||
private loginService: LoginService,
|
private loginService: LoginService,
|
||||||
private authService: AuthService,
|
private authService: AuthService
|
||||||
private router: Router
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onSubmit(): void {
|
onSubmit(): void {
|
||||||
this.loginError = undefined;
|
this.loginService.login(this.model).subscribe((pUser: User) => {
|
||||||
|
console.log('Login success.');
|
||||||
this.loginService.login(this.model).subscribe(user => {
|
this.authService.setAuthenticated(pUser);
|
||||||
this.authService.setToken(user.token);
|
|
||||||
this.authService.setUser(user);
|
|
||||||
this.router.navigate(['/myPosts']);
|
this.router.navigate(['/myPosts']);
|
||||||
}, error => {
|
}, (error) => {
|
||||||
this.setMessage('Email ou password incorrect.');
|
if (error.status === 401) {
|
||||||
|
console.log('Login attempt failed.');
|
||||||
|
this.setMessage('Adresse email ou mot de passe incorrect.');
|
||||||
|
} else {
|
||||||
|
console.error('Error during login attempt.', error);
|
||||||
|
this.setMessage('Une erreur est survenue lors de la connexion.');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
import { User } from '../core/entities';
|
import { User } from '../core/entities';
|
||||||
import { environment } from '../../environments/environment';
|
import { Observable } from 'rxjs';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
const LOGIN_URL = environment.apiUrl + '/api/account/login';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LoginService {
|
export class LoginService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(
|
||||||
|
private http: HttpClient
|
||||||
|
) {}
|
||||||
|
|
||||||
login(user: User): Observable<User> {
|
public login(user: User): Observable<User> {
|
||||||
return this.http.post<User>(LOGIN_URL, user);
|
return this.http.post<User>('/api/account/login', user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/main/ts/src/app/not-found/not-found.component.ts
Executable file → Normal file
0
src/main/ts/src/app/not-found/not-found.component.ts
Executable file → Normal file
0
src/main/ts/src/app/posts/byCategory/by-category.component.html
Executable file → Normal file
0
src/main/ts/src/app/posts/byCategory/by-category.component.html
Executable file → Normal file
0
src/main/ts/src/app/posts/byCategory/by-category.component.ts
Executable file → Normal file
0
src/main/ts/src/app/posts/byCategory/by-category.component.ts
Executable file → Normal file
11
src/main/ts/src/app/posts/byCategory/by-category.service.ts
Executable file → Normal file
11
src/main/ts/src/app/posts/byCategory/by-category.service.ts
Executable file → Normal file
@@ -1,23 +1,18 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
import { Post, Category } from '../../core/entities';
|
import { Post, Category } from '../../core/entities';
|
||||||
|
|
||||||
const CATEGORIES_URL = environment.apiUrl + '/api/categories';
|
|
||||||
const POSTS_URL = environment.apiUrl + '/api/posts';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ByCategoryService {
|
export class ByCategoryService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
getCategoryById(categoryId: number): Observable<Category> {
|
getCategoryById(categoryId: number): Observable<Category> {
|
||||||
return this.http.get<Category>(`${CATEGORIES_URL}/${categoryId}`);
|
return this.http.get<Category>(`/api/categories/${categoryId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPostsByCategory(category: Category): Observable<Array<Post>> {
|
getPostsByCategory(category: Category): Observable<Array<Post>> {
|
||||||
return this.http.get<Array<Post>>(`${POSTS_URL}/byCategory/${category.id}`);
|
return this.http.get<Array<Post>>(`/api/posts/byCategory/${category.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/main/ts/src/app/posts/create-update/create-update-post.component.html
Executable file → Normal file
46
src/main/ts/src/app/posts/create-update/create-update-post.component.html
Executable file → Normal file
@@ -18,7 +18,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div *ngIf="activatedTab === 'Édition'">
|
<div *ngIf="activatedTab === 'Édition'">
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="title"
|
id="title"
|
||||||
name="title"
|
name="title"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -26,12 +26,12 @@
|
|||||||
[(ngModel)]="model.title"
|
[(ngModel)]="model.title"
|
||||||
#title="ngModel"
|
#title="ngModel"
|
||||||
data-error="Veuillez saisir un titre d'article"
|
data-error="Veuillez saisir un titre d'article"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="title">Titre de l'article</label>
|
<label for="title">Titre de l'article</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="image"
|
id="image"
|
||||||
name="image"
|
name="image"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -39,12 +39,12 @@
|
|||||||
[(ngModel)]="model.image"
|
[(ngModel)]="model.image"
|
||||||
#image="ngModel"
|
#image="ngModel"
|
||||||
data-error="Veuillez saisir une adresse URL ou uploader une image"
|
data-error="Veuillez saisir une adresse URL ou uploader une image"
|
||||||
data-success=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="image">Image de l'article</label>
|
<label for="image">Image de l'article</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="description"
|
id="description"
|
||||||
name="description"
|
name="description"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -52,13 +52,13 @@
|
|||||||
[(ngModel)]="model.description"
|
[(ngModel)]="model.description"
|
||||||
#description="ngModel"
|
#description="ngModel"
|
||||||
data-error="Veuillez saisir la description de l'article"
|
data-error="Veuillez saisir la description de l'article"
|
||||||
data-success=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="description">Description de l'article</label>
|
<label for="description">Description de l'article</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mb-3 wrap">
|
<div class="input-group mb-3 wrap">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select id="category" class="select-text" [(ngModel)]="model.category" required>
|
<select id="category" class="select-text" [(ngModel)]="model.category" [compareWith]="compareCategories" required>
|
||||||
<option value="" disabled selected></option>
|
<option value="" disabled selected></option>
|
||||||
<option *ngFor="let category of listCategories" [ngValue]="category">{{category.name}}</option>
|
<option *ngFor="let category of listCategories" [ngValue]="category">{{category.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -73,19 +73,19 @@
|
|||||||
(click)="injectHeader('h1')"
|
(click)="injectHeader('h1')"
|
||||||
mdbTooltip="Titre 1"
|
mdbTooltip="Titre 1"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
mdbRippleRadius>H1</button>
|
mdbRippleRadius><b>H1</b></button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-floating waves-light"
|
class="btn btn-floating waves-light"
|
||||||
(click)="injectHeader('h2')"
|
(click)="injectHeader('h2')"
|
||||||
mdbTooltip="Titre 2"
|
mdbTooltip="Titre 2"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
mdbRippleRadius>H2</button>
|
mdbRippleRadius><b>H2</b></button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-floating waves-light"
|
class="btn btn-floating waves-light"
|
||||||
(click)="injectHeader('h3')"
|
(click)="injectHeader('h3')"
|
||||||
mdbTooltip="Titre 3"
|
mdbTooltip="Titre 3"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
mdbRippleRadius>H3</button>
|
mdbRippleRadius><b>H3</b></button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-floating waves-light"
|
class="btn btn-floating waves-light"
|
||||||
(click)="openImagesModal()"
|
(click)="openImagesModal()"
|
||||||
@@ -112,20 +112,20 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<textarea mdbActive
|
<textarea mdbInputDirective
|
||||||
id="text"
|
id="text"
|
||||||
name="text"
|
name="text"
|
||||||
type="text"
|
type="text"
|
||||||
class="md-textarea"
|
class="md-textarea form-control"
|
||||||
[(ngModel)]="model.text"
|
[(ngModel)]="model.text"
|
||||||
#text="ngModel"
|
#text="ngModel"
|
||||||
data-error="Veuillez saisir le contenu de l'article"
|
data-error="Veuillez saisir le contenu de l'article"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required>
|
required>
|
||||||
</textarea>
|
</textarea>
|
||||||
<label for="text">Contenu de l'article</label>
|
<label for="text">Contenu de l'article</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
<!-- <div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="white-text mb-0">{{modelError}}</p>
|
<p class="white-text mb-0">{{modelError}}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="white-text mb-0">{{result}}</p>
|
<p class="white-text mb-0">{{result}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="activatedTab === 'Aperçu'">
|
<div *ngIf="activatedTab === 'Aperçu'">
|
||||||
<app-spinner *ngIf="!parsedPost"></app-spinner>
|
<app-spinner *ngIf="!parsedPost"></app-spinner>
|
||||||
@@ -148,6 +148,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="white-text mb-0">{{modelError}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="resultMsg" class="card green lighten-2 text-center z-depth-2" >
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="white-text mb-0">{{result}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<a routerLink="/myPosts">Annuler</a>
|
<a routerLink="/myPosts">Annuler</a>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
@@ -183,12 +193,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form" style="margin-top: 15px; margin-bottom: 0;">
|
<div class="md-form" style="margin-top: 15px; margin-bottom: 0;">
|
||||||
<textarea mdbActive
|
<textarea mdbInputDirective
|
||||||
id="codeTmp"
|
id="codeTmp"
|
||||||
name="codeTmp"
|
name="codeTmp"
|
||||||
type="text"
|
type="text"
|
||||||
class="md-textarea"
|
class="md-textarea form-control"
|
||||||
[(ngModel)]="codeTmp"
|
[(ngModel)]="codeTmp"
|
||||||
|
data-error="Veuillez écrire ou coller votre extrait de code dans cette zone de saisie"
|
||||||
|
[validateSuccess]="false"
|
||||||
required>
|
required>
|
||||||
</textarea>
|
</textarea>
|
||||||
<label for="codeTmp">Extrait de code</label>
|
<label for="codeTmp">Extrait de code</label>
|
||||||
|
|||||||
7
src/main/ts/src/app/posts/create-update/create-update-post.component.scss
Executable file → Normal file
7
src/main/ts/src/app/posts/create-update/create-update-post.component.scss
Executable file → Normal file
@@ -18,11 +18,6 @@
|
|||||||
border-bottom: 4px solid white;
|
border-bottom: 4px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
|
||||||
height: 250px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select {
|
.custom-select {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
border-bottom: 1px #aaa solid;
|
border-bottom: 1px #aaa solid;
|
||||||
@@ -68,6 +63,8 @@ textarea {
|
|||||||
|
|
||||||
#text {
|
#text {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
height: 250px;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
#resultMsg, #errorMsg {
|
#resultMsg, #errorMsg {
|
||||||
|
|||||||
23
src/main/ts/src/app/posts/create-update/create-update-post.component.ts
Executable file → Normal file
23
src/main/ts/src/app/posts/create-update/create-update-post.component.ts
Executable file → Normal file
@@ -1,14 +1,13 @@
|
|||||||
import { Component, OnInit, SecurityContext, ViewChild } from '@angular/core';
|
import { Component, OnInit, SecurityContext, ViewChild } from '@angular/core';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { Router, ActivatedRoute, RoutesRecognized } from '@angular/router';
|
import { Router, ActivatedRoute, RoutesRecognized, NavigationEnd } from '@angular/router';
|
||||||
|
|
||||||
import { Post, Category, Image } from '../../core/entities';
|
import { Post, Category, Image } from '../../core/entities';
|
||||||
import { AuthService } from '../../core/services/auth.service';
|
import { AuthService } from '../../core/services/auth.service';
|
||||||
import { CreateUpdatePostService } from './create-update-post.service';
|
import { CreateUpdatePostService } from './create-update-post.service';
|
||||||
|
|
||||||
import 'rxjs/add/operator/filter';
|
import { filter, pairwise } from 'rxjs/operators';
|
||||||
import 'rxjs/add/operator/pairwise';
|
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
import { HttpEventType, HttpResponse } from '@angular/common/http';
|
import { HttpEventType, HttpResponse } from '@angular/common/http';
|
||||||
|
|
||||||
enum Tabs {
|
enum Tabs {
|
||||||
@@ -78,12 +77,10 @@ export class CreateUpdatePostComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.router.events.filter(e => e instanceof RoutesRecognized).pairwise().subscribe((event: any[]) => {
|
// FIXME: The message isn't shown and the method ngOnInit is too much called, also during others components navigation.
|
||||||
if (event[0].urlAfterRedirects === '/posts/new') {
|
this.router.events.pipe(filter(e => e instanceof RoutesRecognized), pairwise()).subscribe((events: any) => {
|
||||||
this.result = 'Article créé.';
|
if (events[0].urlAfterRedirects === '/posts/new') {
|
||||||
setTimeout(() => {
|
this.setMessage('Article créé.', false);
|
||||||
this.result = undefined;
|
|
||||||
}, 3500);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -196,7 +193,7 @@ export class CreateUpdatePostComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getLinkSrc(pLink: string): string {
|
getLinkSrc(pLink: string): string {
|
||||||
return `${environment.apiUrl}/api/images/${pLink}`;
|
return `/api/images/${pLink}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
openNewImageInput(): void {
|
openNewImageInput(): void {
|
||||||
@@ -229,4 +226,8 @@ export class CreateUpdatePostComponent implements OnInit {
|
|||||||
this.injectElement(imgTag, imgTag.length);
|
this.injectElement(imgTag, imgTag.length);
|
||||||
this.imagesModal.hide();
|
this.imagesModal.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compareCategories(cat1: Category, cat2: Category): boolean {
|
||||||
|
return cat1 && cat2 ? cat1.id === cat2.id : cat1 === cat2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/main/ts/src/app/posts/create-update/create-update-post.service.ts
Executable file → Normal file
24
src/main/ts/src/app/posts/create-update/create-update-post.service.ts
Executable file → Normal file
@@ -1,13 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { HttpClient, HttpEvent, HttpRequest } from '@angular/common/http';
|
import { HttpClient, HttpEvent, HttpRequest } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { Post, Category, Image } from '../../core/entities';
|
import { Post, Category, Image } from '../../core/entities';
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
|
|
||||||
const IMAGES_URL = environment.apiUrl + '/api/images';
|
|
||||||
const POSTS_URL = environment.apiUrl + '/api/posts';
|
|
||||||
const CATEGORIES_URL = environment.apiUrl + '/api/categories';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CreateUpdatePostService {
|
export class CreateUpdatePostService {
|
||||||
@@ -15,27 +9,27 @@ export class CreateUpdatePostService {
|
|||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
processPreview(post: Post): Observable<Post> {
|
processPreview(post: Post): Observable<Post> {
|
||||||
return this.http.post<Post>(`${POSTS_URL}/preview`, post);
|
return this.http.post<Post>(`/api/posts/preview`, post);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategories(): Observable<Array<Category>> {
|
getCategories(): Observable<Array<Category>> {
|
||||||
return this.http.get<Array<Category>>(`${CATEGORIES_URL}/`);
|
return this.http.get<Array<Category>>(`/api/categories/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPost(post: Post): Observable<Post> {
|
addPost(post: Post): Observable<Post> {
|
||||||
return this.http.post<Post>(`${POSTS_URL}/`, post);
|
return this.http.post<Post>(`/api/posts/`, post);
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePost(post: Post): Observable<Post> {
|
updatePost(post: Post): Observable<Post> {
|
||||||
return this.http.put<Post>(`${POSTS_URL}/`, post);
|
return this.http.put<Post>(`/api/posts/`, post);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPost(postKey: string): Observable<Post> {
|
getPost(postKey: string): Observable<Post> {
|
||||||
return this.http.get<Post>(`${POSTS_URL}/${postKey}/source`);
|
return this.http.get<Post>(`/api/posts/${postKey}/source`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getImages(): Observable<Array<Image>> {
|
getImages(): Observable<Array<Image>> {
|
||||||
return this.http.get<Array<Image>>(`${IMAGES_URL}/myImages`);
|
return this.http.get<Array<Image>>(`/api/images/myImages`);
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadPicture(file: File): Observable<HttpEvent<{}>> {
|
uploadPicture(file: File): Observable<HttpEvent<{}>> {
|
||||||
@@ -44,7 +38,7 @@ export class CreateUpdatePostService {
|
|||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
return this.http.request(new HttpRequest(
|
return this.http.request(new HttpRequest(
|
||||||
'POST', IMAGES_URL, formData, {
|
'POST', '/api/images', formData, {
|
||||||
reportProgress: true,
|
reportProgress: true,
|
||||||
responseType: 'text'
|
responseType: 'text'
|
||||||
}
|
}
|
||||||
@@ -52,6 +46,6 @@ export class CreateUpdatePostService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getImageDetails(imageLink: string): Observable<Image> {
|
getImageDetails(imageLink: string): Observable<Image> {
|
||||||
return this.http.get<Image>(`${IMAGES_URL}/${imageLink}/details`);
|
return this.http.get<Image>(`/api/images/${imageLink}/details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
<div *ngIf="listPosts" class="col-lg-8 offset-lg-2">
|
<div *ngIf="listPosts" class="col-lg-8 offset-lg-2">
|
||||||
<app-post-card *ngFor="let post of listPosts" [post]="post"></app-post-card>
|
<app-post-card *ngFor="let post of listPosts" [post]="post"></app-post-card>
|
||||||
</div>
|
</div>
|
||||||
|
<span *ngIf="listPosts?.length === 0">
|
||||||
|
Aucun article.
|
||||||
|
</span>
|
||||||
<a routerLink="/posts/new" class="fixed-action-btn green white-text">+</a>
|
<a routerLink="/posts/new" class="fixed-action-btn green white-text">+</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
|
||||||
import { Post } from '../../core/entities';
|
import { Post } from '../../core/entities';
|
||||||
|
|
||||||
const POSTS_URL = environment.apiUrl + '/api/posts';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MyPostsService {
|
export class MyPostsService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
getMyPosts(): Observable<Array<Post>> {
|
getMyPosts(): Observable<Array<Post>> {
|
||||||
return this.http.get<Array<Post>>(`${POSTS_URL}/myPosts`);
|
return this.http.get<Array<Post>>(`/api/posts/myPosts`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/main/ts/src/app/posts/post.component.html
Executable file → Normal file
4
src/main/ts/src/app/posts/post.component.html
Executable file → Normal file
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<a *ngIf="owned" class="btn-card-floating waves-light white-text"
|
<a *ngIf="owned" class="btn-card-floating waves-light white-text"
|
||||||
routerLink="/posts/update/{{post.key}}">
|
routerLink="/posts/update/{{post.key}}">
|
||||||
<i class="fa fa-pencil"></i>
|
<i class="fa fa-pen"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
[mdbTooltip]="post.author.name"
|
[mdbTooltip]="post.author.name"
|
||||||
placement="bottom"/>
|
placement="bottom"/>
|
||||||
Article écrit par {{post.author.name}}
|
Article écrit par {{post.author.name}}
|
||||||
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
|
<span class="creation-date-area">({{post.creationDate | date:'HH:mm:ss dd/MM/yyyy'}})</span>
|
||||||
<button *ngIf="owned" type="button" class="btn btn-danger waves-light float-right" (click)="alertDelete.show()" mdbRippleRadius>
|
<button *ngIf="owned" type="button" class="btn btn-danger waves-light float-right" (click)="alertDelete.show()" mdbRippleRadius>
|
||||||
<i class="fa fa-trash mr-1"></i> Supprimer
|
<i class="fa fa-trash mr-1"></i> Supprimer
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
3
src/main/ts/src/app/posts/post.component.ts
Executable file → Normal file
3
src/main/ts/src/app/posts/post.component.ts
Executable file → Normal file
@@ -6,7 +6,6 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
|||||||
import { Post, User } from '../core/entities';
|
import { Post, User } from '../core/entities';
|
||||||
import { PostService } from './post.service';
|
import { PostService } from './post.service';
|
||||||
import { AuthService } from '../core/services/auth.service';
|
import { AuthService } from '../core/services/auth.service';
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
declare let Prism: any;
|
declare let Prism: any;
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@ export class PostComponent implements OnInit {
|
|||||||
|
|
||||||
getAvatarUrl(): string {
|
getAvatarUrl(): string {
|
||||||
return this.post.author.image
|
return this.post.author.image
|
||||||
? `${environment.apiUrl}/api/images/loadAvatar/${this.post.author.image}`
|
? `./api/images/loadAvatar/${this.post.author.image}`
|
||||||
: './assets/images/default_user.png';
|
: './assets/images/default_user.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/main/ts/src/app/posts/post.service.ts
Executable file → Normal file
10
src/main/ts/src/app/posts/post.service.ts
Executable file → Normal file
@@ -1,22 +1,18 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
import { Post } from '../core/entities';
|
import { Post } from '../core/entities';
|
||||||
|
|
||||||
const POSTS_URL = environment.apiUrl + '/api/posts';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PostService {
|
export class PostService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
getPost(postKey: string): Observable<Post> {
|
getPost(postKey: string): Observable<Post> {
|
||||||
return this.http.get<Post>(`${POSTS_URL}/${postKey}`);
|
return this.http.get<Post>(`/api/posts/${postKey}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePost(postToDelete: Post): Observable<any> {
|
deletePost(postToDelete: Post): Observable<any> {
|
||||||
return this.http.delete(`${POSTS_URL}/${postToDelete.key}`);
|
return this.http.delete(`/api/posts/${postToDelete.key}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/main/ts/src/app/search/search.component.ts
Executable file → Normal file
0
src/main/ts/src/app/search/search.component.ts
Executable file → Normal file
8
src/main/ts/src/app/search/search.service.ts
Executable file → Normal file
8
src/main/ts/src/app/search/search.service.ts
Executable file → Normal file
@@ -1,18 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
import { Post } from '../core/entities';
|
import { Post } from '../core/entities';
|
||||||
|
|
||||||
const POSTS_URL = environment.apiUrl + '/api/posts';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchService {
|
export class SearchService {
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
search(searchCriteria: string): Observable<Array<Post>> {
|
search(searchCriteria: string): Observable<Array<Post>> {
|
||||||
return this.http.get<Array<Post>>(`${POSTS_URL}/search/${searchCriteria}`);
|
return this.http.get<Array<Post>>(`/api/posts/search/${searchCriteria}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/main/ts/src/app/signin/signin.component.html
Executable file → Normal file
16
src/main/ts/src/app/signin/signin.component.html
Executable file → Normal file
@@ -4,7 +4,7 @@
|
|||||||
<form (ngSubmit)="onSubmit()" #signinForm="ngForm">
|
<form (ngSubmit)="onSubmit()" #signinForm="ngForm">
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<i class="fa fa-id-badge prefix grey-text"></i>
|
<i class="fa fa-id-badge prefix grey-text"></i>
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="name"
|
id="name"
|
||||||
name="name"
|
name="name"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -12,13 +12,13 @@
|
|||||||
[(ngModel)]="model.name"
|
[(ngModel)]="model.name"
|
||||||
#name="ngModel"
|
#name="ngModel"
|
||||||
data-error="Veuillez saisir un nom d'utilisateur"
|
data-error="Veuillez saisir un nom d'utilisateur"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="name">Nom d'utilisateur</label>
|
<label for="name">Nom d'utilisateur</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<i class="fa fa-envelope prefix grey-text"></i>
|
<i class="fa fa-envelope prefix grey-text"></i>
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
@@ -26,13 +26,13 @@
|
|||||||
[(ngModel)]="model.email"
|
[(ngModel)]="model.email"
|
||||||
#email="ngModel"
|
#email="ngModel"
|
||||||
data-error="Veuillez saisir une adresse email valide"
|
data-error="Veuillez saisir une adresse email valide"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="email">Email</label>
|
<label for="email">Email</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<i class="fa fa-lock prefix grey-text"></i>
|
<i class="fa fa-lock prefix grey-text"></i>
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -40,20 +40,20 @@
|
|||||||
[(ngModel)]="model.password"
|
[(ngModel)]="model.password"
|
||||||
#password="ngModel"
|
#password="ngModel"
|
||||||
data-error="Veuillez saisir votre mot de passe"
|
data-error="Veuillez saisir votre mot de passe"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="password">Password</label>
|
<label for="password">Password</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form">
|
<div class="md-form">
|
||||||
<i class="fa fa-lock prefix grey-text"></i>
|
<i class="fa fa-lock prefix grey-text"></i>
|
||||||
<input mdbActive
|
<input mdbInputDirective
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
[(ngModel)]="confirmPassword"
|
[(ngModel)]="confirmPassword"
|
||||||
data-error="Veuillez confirmer votre mot de passe"
|
data-error="Veuillez confirmer votre mot de passe"
|
||||||
data-sucess=""
|
[validateSuccess]="false"
|
||||||
required />
|
required />
|
||||||
<label for="confirmPassword">Confirmez votre mot de passe</label>
|
<label for="confirmPassword">Confirmez votre mot de passe</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
3
src/main/ts/src/app/signin/signin.component.ts
Executable file → Normal file
3
src/main/ts/src/app/signin/signin.component.ts
Executable file → Normal file
@@ -35,9 +35,10 @@ export class SigninComponent {
|
|||||||
|
|
||||||
onSubmit(): void {
|
onSubmit(): void {
|
||||||
if (this.confirmPassword && this.confirmPassword === this.model.password) {
|
if (this.confirmPassword && this.confirmPassword === this.model.password) {
|
||||||
this.signinService.signin(this.model).subscribe(user => {
|
this.signinService.signin(this.model).subscribe(() => {
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/login']);
|
||||||
}, error => {
|
}, error => {
|
||||||
|
// FIXME: Type the error to get the status.
|
||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
case 409:
|
case 409:
|
||||||
this.setMessage('L\'adresse mail saisie n\'est pas disponible');
|
this.setMessage('L\'adresse mail saisie n\'est pas disponible');
|
||||||
|
|||||||
8
src/main/ts/src/app/signin/signin.service.ts
Executable file → Normal file
8
src/main/ts/src/app/signin/signin.service.ts
Executable file → Normal file
@@ -1,11 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { User } from '../core/entities';
|
import { User } from '../core/entities';
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
const SIGNIN_URL = environment.apiUrl + '/api/account/signin';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SigninService {
|
export class SigninService {
|
||||||
@@ -13,6 +9,6 @@ export class SigninService {
|
|||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
signin(user: User): Observable<User> {
|
signin(user: User): Observable<User> {
|
||||||
return this.http.post<User>(SIGNIN_URL, user);
|
return this.http.post<User>('/api/account/signin', user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/main/ts/src/app/version-revisions/version-revisions.component.html
Executable file → Normal file
0
src/main/ts/src/app/version-revisions/version-revisions.component.html
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user