Compare commits
7 Commits
8ec806440b
...
16471e9962
| Author | SHA1 | Date | |
|---|---|---|---|
| 16471e9962 | |||
| 6e7ae115e5 | |||
| 95af38bf72 | |||
| 5e5fbd9377 | |||
| 84b1dd6352 | |||
| 1f8a11a73c | |||
| 7f0ff8157f |
@@ -8,7 +8,6 @@ import org.springframework.context.annotation.ComponentScan;
|
||||
@SpringBootApplication
|
||||
@EnableAutoConfiguration
|
||||
public class CodikiApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CodikiApplication.class, args);
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ public class AccountController {
|
||||
}
|
||||
|
||||
@PutMapping("/")
|
||||
public void update(@RequestBody final UserDTO pUser, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
accountService.updateUser(pUser, pRequest, pResponse, pPrincipal);
|
||||
public void update(@RequestBody UserDTO pUser, HttpServletResponse pResponse, Principal pPrincipal)
|
||||
throws IOException {
|
||||
accountService.updateUser(pUser, pResponse, pPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
package org.codiki.account;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.security.Principal;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.codiki.core.entities.dto.ImageDTO;
|
||||
import org.codiki.core.entities.dto.PasswordWrapperDTO;
|
||||
import org.codiki.core.entities.dto.UserDTO;
|
||||
@@ -22,38 +10,30 @@ import org.codiki.core.security.CustomAuthenticationProvider;
|
||||
import org.codiki.core.services.FileUploadService;
|
||||
import org.codiki.core.services.UserService;
|
||||
import org.codiki.core.utils.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AccountService {
|
||||
/** Logger. */
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FileUploadService.class);
|
||||
private final CustomAuthenticationProvider authenticationProvider;
|
||||
private final UserService userService;
|
||||
private final UserRepository userRepository;
|
||||
private final FileUploadService fileUploadService;
|
||||
private final ImageRepository imageRepository;
|
||||
|
||||
private CustomAuthenticationProvider authenticationProvider;
|
||||
|
||||
private UserService userService;
|
||||
|
||||
private UserRepository userRepository;
|
||||
|
||||
private FileUploadService fileUploadService;
|
||||
|
||||
private ImageRepository imageRepository;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param authenticationProvider
|
||||
* @param userService
|
||||
* @param userRepository
|
||||
* @param fileUploadService
|
||||
* @param imageRepository
|
||||
*/
|
||||
/** Constructor. */
|
||||
public AccountService(CustomAuthenticationProvider authenticationProvider,
|
||||
UserService userService,
|
||||
UserRepository userRepository,
|
||||
@@ -93,8 +73,8 @@ public class AccountService {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public String uploadFile(final MultipartFile pFile, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
public String uploadFile(MultipartFile pFile, HttpServletResponse pResponse, Principal pPrincipal)
|
||||
throws IOException {
|
||||
final String avatarFileName = fileUploadService.uploadProfileImage(pFile);
|
||||
|
||||
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||
@@ -117,8 +97,7 @@ public class AccountService {
|
||||
return fileUploadService.loadAvatar(pAvatarFileName);
|
||||
}
|
||||
|
||||
public List<ImageDTO> getUserImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
|
||||
final Principal pPrincipal) throws IOException {
|
||||
public List<ImageDTO> getUserImages(HttpServletResponse pResponse, Principal pPrincipal) throws IOException {
|
||||
List<ImageDTO> result = new LinkedList<>();
|
||||
|
||||
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||
@@ -132,7 +111,7 @@ public class AccountService {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void signin(final User pUser, final HttpServletResponse pResponse) throws IOException {
|
||||
public void signin(User pUser, HttpServletResponse pResponse) throws IOException {
|
||||
if(pUser.getName() == null || pUser.getEmail() == null || pUser.getPassword() == null || "".equals(pUser.getPassword().trim())) {
|
||||
pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
} else if(userRepository.findByEmail(pUser.getEmail()).isPresent()) {
|
||||
@@ -148,8 +127,7 @@ public class AccountService {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUser(final UserDTO pUser, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
public void updateUser(UserDTO pUser, HttpServletResponse pResponse, Principal pPrincipal) throws IOException {
|
||||
final Optional<User> connectedUserOpt = userService.getUserByPrincipal(pPrincipal);
|
||||
if(connectedUserOpt.isPresent()) {
|
||||
final User connectedUser = connectedUserOpt.get();
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package org.codiki.categories;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.codiki.core.entities.dto.CategoryDTO;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.codiki.core.entities.dto.View;
|
||||
import org.codiki.core.entities.persistence.Category;
|
||||
import org.codiki.core.repositories.CategoryRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -14,22 +10,33 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/categories")
|
||||
public class CategoryController {
|
||||
|
||||
@Autowired
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public CategoryDTO findById(@PathVariable("id") final Long pId) {
|
||||
final Optional<Category> result = categoryRepository.findById(pId);
|
||||
return result.isPresent() ? new CategoryDTO(result.get()) : null;
|
||||
private final CategoryRepository categoryRepository;
|
||||
|
||||
/** Constructor. */
|
||||
public CategoryController(CategoryRepository categoryRepository) {
|
||||
this.categoryRepository = categoryRepository;
|
||||
}
|
||||
|
||||
|
||||
@JsonView(View.CategoryDTO.class)
|
||||
@GetMapping("/{id}")
|
||||
public Category findById(@PathVariable("id") final Long pId,
|
||||
HttpServletResponse response) {
|
||||
return categoryRepository.findByIdWithSubCategories(pId)
|
||||
.orElseGet(() -> {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@JsonView(View.CategoryDTO.class)
|
||||
@GetMapping("/")
|
||||
public List<CategoryDTO> getAll() {
|
||||
return StreamSupport.stream(categoryRepository.findAll().spliterator(), false)
|
||||
.map(CategoryDTO::new).collect(Collectors.toList());
|
||||
public List<Category> getAll() {
|
||||
return categoryRepository.findAllWithSubCategories();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package org.codiki.core.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller that catch errors from spring rest or spring security and others, and transform them to JSON response.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/error")
|
||||
public class CustomErrorController implements ErrorController {
|
||||
|
||||
private final ErrorAttributes errorAttributes;
|
||||
|
||||
@Autowired
|
||||
public CustomErrorController(ErrorAttributes errorAttributes) {
|
||||
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
|
||||
this.errorAttributes = errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorPath() {
|
||||
return "/error";
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
public Map<String, Object> error(HttpServletRequest request){
|
||||
Map<String, Object> body = getErrorAttributes(request, getTraceParameter(request));
|
||||
String trace = (String) body.get("trace");
|
||||
if(trace != null){
|
||||
String[] lines = trace.split("\n\t");
|
||||
body.put("trace", lines);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
private boolean getTraceParameter(HttpServletRequest request) {
|
||||
String parameter = request.getParameter("trace");
|
||||
if (parameter == null) {
|
||||
return false;
|
||||
}
|
||||
return !"false".equals(parameter.toLowerCase());
|
||||
}
|
||||
|
||||
private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
||||
return errorAttributes.getErrorAttributes(new ServletWebRequest(request), includeStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -17,21 +17,11 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
@EnableJpaRepositories("org.codiki.core.repositories")
|
||||
@PropertySource("classpath:application.yml")
|
||||
public class JpaConfiguration {
|
||||
|
||||
@Value("${spring.jpa.datasource.driverClassName}")
|
||||
private String driverClassName;
|
||||
|
||||
@Value("${spring.jpa.datasource.url}")
|
||||
private String url;
|
||||
|
||||
@Value("${spring.jpa.datasource.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${spring.jpa.datasource.password}")
|
||||
private String password;
|
||||
|
||||
@Bean(name = "dataSource")
|
||||
public DataSource getDataSource() {
|
||||
public DataSource getDataSource(@Value("${spring.jpa.datasource.driverClassName}") String driverClassName,
|
||||
@Value("${spring.jpa.datasource.url}") String url,
|
||||
@Value("${spring.jpa.datasource.username}") String username,
|
||||
@Value("${spring.jpa.datasource.password}") String password) {
|
||||
return DataSourceBuilder.create()
|
||||
.username(username)
|
||||
.password(password)
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
public class RobotsTxtController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RobotsTxtController.class);
|
||||
|
||||
@RequestMapping(value = "/robots.txt")
|
||||
|
||||
@@ -3,4 +3,5 @@ package org.codiki.core.entities.dto;
|
||||
public class View {
|
||||
public interface UserDTO {}
|
||||
public interface PostDTO {}
|
||||
public interface CategoryDTO {}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@ import org.codiki.core.entities.dto.CategoryDTO;
|
||||
import org.codiki.core.entities.dto.View;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
@Entity
|
||||
@Table(name="category")
|
||||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
@Proxy(lazy = false)
|
||||
public class Category implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -32,10 +34,10 @@ public class Category implements Serializable {
|
||||
/* ******************* */
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@JsonView(View.PostDTO.class)
|
||||
@JsonView({View.PostDTO.class, View.CategoryDTO.class})
|
||||
private Long id;
|
||||
|
||||
@JsonView(View.PostDTO.class)
|
||||
@JsonView({View.PostDTO.class, View.CategoryDTO.class})
|
||||
private String name;
|
||||
|
||||
/* ******************* */
|
||||
@@ -44,7 +46,8 @@ public class Category implements Serializable {
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "creator_id")
|
||||
protected User creator;
|
||||
|
||||
|
||||
@JsonView({View.CategoryDTO.class})
|
||||
@OneToMany(mappedBy = "mainCategory")
|
||||
private List<SubCategory> listSubCategories;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -19,6 +21,7 @@ import javax.persistence.TemporalType;
|
||||
|
||||
@Entity
|
||||
@Table(name="comment")
|
||||
@Proxy(lazy = false)
|
||||
public class Comment implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -17,6 +19,7 @@ import javax.persistence.TemporalType;
|
||||
|
||||
@Entity
|
||||
@Table(name="comment_history")
|
||||
@Proxy(lazy = false)
|
||||
public class CommentHistory implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -16,6 +18,7 @@ import javax.persistence.TemporalType;
|
||||
|
||||
@Entity
|
||||
@Table(name="image")
|
||||
@Proxy(lazy = false)
|
||||
public class Image implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -25,9 +25,11 @@ import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.GenerationTime;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
@Entity
|
||||
@Table(name="post")
|
||||
@Proxy(lazy = false)
|
||||
public class Post implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -17,6 +19,7 @@ import javax.persistence.TemporalType;
|
||||
|
||||
@Entity
|
||||
@Table(name="post_history")
|
||||
@Proxy(lazy = false)
|
||||
public class PostHistory implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@ import javax.persistence.Table;
|
||||
import org.codiki.core.entities.dto.View;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
@Entity
|
||||
@Table(name="role")
|
||||
@Proxy(lazy = false)
|
||||
public class Role implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
@@ -10,6 +13,7 @@ import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sub_category")
|
||||
@Proxy(lazy = false)
|
||||
public class SubCategory extends Category {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -34,4 +38,15 @@ public class SubCategory extends Category {
|
||||
public List<Post> getListPosts() {
|
||||
return listPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Category#getListSubCategories}.
|
||||
*
|
||||
* This getter is overrided to set {@link JsonView} annotation to avoid Lazy initialization for subcategories.
|
||||
*/
|
||||
@JsonView()
|
||||
@Override
|
||||
public List<SubCategory> getListSubCategories() {
|
||||
return super.getListSubCategories();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.GenerationTime;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
@Entity
|
||||
@Table(name="`user`")
|
||||
@Proxy(lazy = false)
|
||||
public class User implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
@@ -16,6 +18,7 @@ import javax.persistence.Table;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="version")
|
||||
@Proxy(lazy = false)
|
||||
public class Version implements Serializable {
|
||||
private static final long serialVersionUID = -8803744005903941330L;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.codiki.core.entities.persistence;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
@@ -13,6 +15,7 @@ import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name="version_revision")
|
||||
@Proxy(lazy = false)
|
||||
public class VersionRevision implements Serializable {
|
||||
private static final long serialVersionUID = 1837590467941917225L;
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package org.codiki.core.repositories;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.codiki.core.entities.persistence.Category;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface CategoryRepository extends CrudRepository<Category, Long> {
|
||||
@Query("SELECT c FROM Category c JOIN FETCH c.listSubCategories")
|
||||
@Query("SELECT c FROM Category c LEFT JOIN FETCH c.listSubCategories WHERE c.id = :id")
|
||||
Optional<Category> findByIdWithSubCategories(@Param("id") Long id);
|
||||
|
||||
@Query("SELECT DISTINCT(c) FROM Category c LEFT JOIN FETCH c.listSubCategories ORDER BY c.name")
|
||||
List<Category> findAllWithSubCategories();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.springframework.stereotype.Component;
|
||||
public class CorsFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
public void init(FilterConfig filterConfig) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@@ -40,5 +40,4 @@ public class CorsFilter implements Filter {
|
||||
public void destroy() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@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>());
|
||||
authentication.getCredentials(), new ArrayList<>());
|
||||
|
||||
// Set the auth bean in spring security context
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
@@ -29,5 +27,4 @@ public class CustomAuthenticationProvider implements AuthenticationProvider {
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return authentication.equals(UsernamePasswordAuthenticationToken.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
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;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Authentication entry point configured in
|
||||
* {@link SecurityConfiguration#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)}
|
||||
|
||||
@@ -26,11 +26,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
private CustomAuthenticationProvider authenticationProvider;
|
||||
private RestAuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param authenticationProvider
|
||||
* @param authenticationEntryPoint
|
||||
*/
|
||||
/** Constructor. */
|
||||
public SecurityConfiguration(CustomAuthenticationProvider authenticationProvider,
|
||||
RestAuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
package org.codiki.core.services;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.codiki.core.utils.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -18,20 +9,31 @@ import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class FileUploadService {
|
||||
/** Logger. */
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FileUploadService.class);
|
||||
/** Length of uploaded file names. */
|
||||
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;
|
||||
|
||||
|
||||
private final String folderUpload;
|
||||
private final String folderProfileImages;
|
||||
private final String folderImages;
|
||||
|
||||
public FileUploadService(@Value("${codiki.files.upload}") String folderUpload,
|
||||
@Value("${codiki.files.profile-images}") String folderProfileImages,
|
||||
@Value("${codiki.files.images}")String folderImages) {
|
||||
this.folderUpload = folderUpload;
|
||||
this.folderProfileImages = folderProfileImages;
|
||||
this.folderImages = folderImages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the destination file name, which is a random string with 30 char
|
||||
* length.
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ParserService {
|
||||
|
||||
private static final String REG_CODE = "\\[code lg="([a-z]+)"\\](.*)\\[\\/code\\]\\n";
|
||||
private static final String REG_CODE_REPLACE = "<pre class=\"line-numbers\"><code class=\"language-\\1\">\\2</code></pre>";
|
||||
private static final String REG_IMAGES = "\\[img src="([^\"| ]+)"( alt="([^\"| ]+)")? \\/\\]";
|
||||
@@ -17,12 +16,12 @@ public class ParserService {
|
||||
private static final String REG_LINKS = "\\[link href="([^\"| ]+)" txt="([^\"| ]+)" \\/\\]";
|
||||
private static final String REG_LINKS_REPLACE = "<a href=\"\\1\">\\2</a>";
|
||||
|
||||
static final Pattern PATTERN_CODE;
|
||||
static final Pattern PATTERN_CODE_REPLACE;
|
||||
static final Pattern PATTERN_IMAGES;
|
||||
static final Pattern PATTERN_IMAGES_REPLACE;
|
||||
static final Pattern PATTERN_LINKS;
|
||||
static final Pattern PATTERN_LINKS_REPLACE;
|
||||
private static final Pattern PATTERN_CODE;
|
||||
private static final Pattern PATTERN_CODE_REPLACE;
|
||||
private static final Pattern PATTERN_IMAGES;
|
||||
private static final Pattern PATTERN_IMAGES_REPLACE;
|
||||
private static final Pattern PATTERN_LINKS;
|
||||
private static final Pattern PATTERN_LINKS_REPLACE;
|
||||
|
||||
static {
|
||||
PATTERN_CODE = Pattern.compile(REG_CODE);
|
||||
@@ -45,7 +44,7 @@ public class ParserService {
|
||||
return pSource.replace("£ø", "$");
|
||||
}
|
||||
|
||||
protected String parseHeaders(final String pSource) {
|
||||
String parseHeaders(final String pSource) {
|
||||
String result = pSource;
|
||||
for(int i = 1 ; i <= 3 ; i++) {
|
||||
result = parseHeadersHX(result, i);
|
||||
@@ -53,7 +52,7 @@ public class ParserService {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String parseHeadersHX(final String pSource, final int pNumHeader) {
|
||||
String parseHeadersHX(final String pSource, final int pNumHeader) {
|
||||
String result = pSource;
|
||||
|
||||
// (.*)(\[hX\](.*)\[\/hX\])+(.*)
|
||||
@@ -73,11 +72,11 @@ public class ParserService {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String parseBackSpaces(final String pSource) {
|
||||
String parseBackSpaces(final String pSource) {
|
||||
return pSource.replaceAll("\r?\n", "<br/>").replaceAll("\\[\\/code\\]<br\\/><br\\/>", "[/code]\n");
|
||||
}
|
||||
|
||||
protected String parseImages(final String pSource) {
|
||||
String parseImages(final String pSource) {
|
||||
String result = pSource;
|
||||
|
||||
Matcher matcher = PATTERN_IMAGES.matcher(result);
|
||||
@@ -96,7 +95,7 @@ public class ParserService {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String parseLinks(final String pSource) {
|
||||
String parseLinks(final String pSource) {
|
||||
String result = pSource;
|
||||
|
||||
Matcher matcher = PATTERN_LINKS.matcher(result);
|
||||
|
||||
@@ -16,16 +16,19 @@ import javax.swing.text.html.Option;
|
||||
@Service
|
||||
public class UserService {
|
||||
private static final String MSG_BAD_CREDENTIALS = "Adresse email ou mot de passe incorrect.";
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserService(UserRepository userRepository) {
|
||||
this.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())) {
|
||||
if(optUser.isEmpty() || !StringUtils.compareHash(password, optUser.get().getPassword())) {
|
||||
throw new BadCredentialsException(MSG_BAD_CREDENTIALS);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DateUtils {
|
||||
public static final String FORMAT_DEFAULT = "dd/MM/yyyy HH:mm:ss";
|
||||
private static final String FORMAT_DEFAULT = "dd/MM/yyyy HH:mm:ss";
|
||||
|
||||
public static Date parseDate(String pSource, String pPattern) throws ParseException {
|
||||
Date result = null;
|
||||
@@ -27,7 +27,7 @@ public class DateUtils {
|
||||
return getSimpleDateFormat(FORMAT_DEFAULT).format(pDate);
|
||||
}
|
||||
|
||||
public static SimpleDateFormat getSimpleDateFormat(String pPattern) {
|
||||
private static SimpleDateFormat getSimpleDateFormat(String pPattern) {
|
||||
SimpleDateFormat result = new SimpleDateFormat(pPattern, Locale.FRENCH);
|
||||
result.setLenient(false);
|
||||
return result;
|
||||
|
||||
@@ -52,9 +52,7 @@ public class ImageController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<String> uploadImage(@RequestParam("file") MultipartFile pFile,
|
||||
final HttpServletResponse pResponse,
|
||||
final Principal pPrincipal) throws IOException {
|
||||
public ResponseEntity<String> uploadImage(@RequestParam("file") MultipartFile pFile, final Principal pPrincipal) {
|
||||
ResponseEntity<String> result;
|
||||
try {
|
||||
result = ResponseEntity.status(HttpStatus.OK)
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ImageService {
|
||||
* @param fileUploadService File upload service.
|
||||
* @param imageRepository Image repository.
|
||||
*/
|
||||
public ImageService(UserService userService,
|
||||
ImageService(UserService userService,
|
||||
UserRepository userRepository,
|
||||
FileUploadService fileUploadService,
|
||||
ImageRepository imageRepository) {
|
||||
@@ -86,11 +86,11 @@ public class ImageService {
|
||||
return fileUploadService.loadAvatar(pAvatarFileName);
|
||||
}
|
||||
|
||||
public Optional<Resource> loadImage(final String pImageLink) {
|
||||
Optional<Resource> loadImage(final String pImageLink) {
|
||||
return fileUploadService.loadImage(pImageLink);
|
||||
}
|
||||
|
||||
public List<ImageDTO> getUserImages(final Principal pPrincipal) {
|
||||
List<ImageDTO> getUserImages(final Principal pPrincipal) {
|
||||
return imageRepository.getByUserId(userService.getUserByPrincipal(pPrincipal)
|
||||
.map(User::getId)
|
||||
.orElseThrow(NoSuchElementException::new))
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.codiki.core.entities.dto.PostDTO;
|
||||
@@ -19,7 +18,6 @@ import org.codiki.core.repositories.PostRepository;
|
||||
import org.codiki.core.services.ParserService;
|
||||
import org.codiki.core.services.UserService;
|
||||
import org.codiki.core.utils.StringUtils;
|
||||
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.GetMapping;
|
||||
@@ -35,7 +33,6 @@ import com.fasterxml.jackson.annotation.JsonView;
|
||||
@RestController
|
||||
@RequestMapping("/api/posts")
|
||||
public class PostController {
|
||||
|
||||
private static final int LIMIT_POSTS_HOME = 20;
|
||||
/** Service to parse post content. */
|
||||
private ParserService parserService;
|
||||
@@ -112,14 +109,11 @@ public class PostController {
|
||||
|
||||
@JsonView(View.PostDTO.class)
|
||||
@GetMapping("/myPosts")
|
||||
public List<Post> getMyPosts(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
|
||||
final Principal pPrincipal) throws IOException {
|
||||
public List<Post> getMyPosts(final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
List<Post> result = new LinkedList<>();
|
||||
|
||||
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||
if(connectedUser.isPresent()) {
|
||||
// result = postRepository.getByCreator(connectedUser.get().getId())
|
||||
// .stream().map(PostDTO::new).collect(Collectors.toList());
|
||||
result = postRepository.getByCreator(connectedUser.get().getId());
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
@@ -142,20 +136,21 @@ public class PostController {
|
||||
|
||||
@JsonView(View.PostDTO.class)
|
||||
@PostMapping("/")
|
||||
public Post insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||
return postService.insert(pPost, pRequest, pResponse, pPrincipal).orElse(null);
|
||||
public Post insert(@RequestBody final Post pPost,
|
||||
final HttpServletResponse pResponse,
|
||||
final Principal pPrincipal) {
|
||||
pResponse.setStatus(postService.insert(pPost, pPrincipal));
|
||||
return pPost;
|
||||
}
|
||||
|
||||
@PutMapping("/")
|
||||
public void update(@RequestBody final Post pPost, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
postService.update(pPost, pRequest, pResponse, pPrincipal);
|
||||
public void update(@RequestBody final Post pPost,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
postService.update(pPost, pResponse, pPrincipal);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{postKey}")
|
||||
public void delete(@PathVariable("postKey") final String pPostKey, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
postService.delete(pPostKey, pRequest, pResponse, pPrincipal);
|
||||
public void delete(@PathVariable("postKey") final String pPostKey, final Principal pPrincipal) {
|
||||
postService.delete(pPostKey, pPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,59 @@
|
||||
package org.codiki.posts;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.codiki.core.entities.business.SearchEntity;
|
||||
import org.codiki.core.entities.dto.PostDTO;
|
||||
import org.codiki.core.entities.persistence.Category;
|
||||
import org.codiki.core.entities.persistence.Post;
|
||||
import org.codiki.core.entities.persistence.User;
|
||||
import org.codiki.core.repositories.CategoryRepository;
|
||||
import org.codiki.core.repositories.PostRepository;
|
||||
import org.codiki.core.services.IllegalAccessException;
|
||||
import org.codiki.core.services.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class PostService {
|
||||
static final int SCORE_TITLE = 50;
|
||||
|
||||
static final int SCORE_DESCRIPTION = 5;
|
||||
|
||||
static final int SCORE_TEXT = 1;
|
||||
|
||||
static final int SCORE_CATEGORY = 30;
|
||||
|
||||
static final int SCORE_AUTHOR_NAME = 40;
|
||||
|
||||
static final int SCORE_AUTHOR_EMAIL = 40;
|
||||
private static final int SCORE_TITLE = 50;
|
||||
private static final int SCORE_DESCRIPTION = 5;
|
||||
private static final int SCORE_TEXT = 1;
|
||||
private static final int SCORE_CATEGORY = 30;
|
||||
private static final int SCORE_AUTHOR_NAME = 40;
|
||||
private static final int SCORE_AUTHOR_EMAIL = 40;
|
||||
|
||||
@Autowired
|
||||
private PostRepository postRepository;
|
||||
|
||||
@Autowired
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
public Optional<Post> insert(final PostDTO pPost, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||
Optional<Post> result = Optional.empty();
|
||||
|
||||
final Optional<User> user = userService.getUserByPrincipal(pPrincipal);
|
||||
|
||||
if(user.isPresent()) {
|
||||
final Post postToSave = new Post(pPost);
|
||||
postToSave.setAuthor(user.get());
|
||||
postRepository.save(postToSave);
|
||||
result = Optional.of(postToSave);
|
||||
} else {
|
||||
pResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return result;
|
||||
private final PostRepository postRepository;
|
||||
|
||||
private final CategoryRepository categoryRepository;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
/** Constructor. */
|
||||
public PostService(PostRepository postRepository,
|
||||
CategoryRepository categoryRepository,
|
||||
UserService userService) {
|
||||
this.postRepository = postRepository;
|
||||
this.categoryRepository = categoryRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
public int insert(final Post pPost, final Principal pPrincipal) {
|
||||
return userService.getUserByPrincipal(pPrincipal)
|
||||
.map(user -> {
|
||||
pPost.setCreationDate(new Date());
|
||||
pPost.setAuthor(user);
|
||||
postRepository.save(pPost);
|
||||
return HttpServletResponse.SC_OK;
|
||||
})
|
||||
.orElse(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
public void update(final Post pPost, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
public void update(final Post pPost, final HttpServletResponse pResponse, final Principal pPrincipal)
|
||||
throws IOException {
|
||||
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||
|
||||
if(connectedUser.isPresent() && connectedUser.get().getKey().equals(pPost.getAuthor().getKey())) {
|
||||
@@ -96,23 +83,19 @@ public class PostService {
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(final String pPostKey, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
final Optional<Post> postToDelete = postRepository.getByKey(pPostKey);
|
||||
if(postToDelete.isPresent()) {
|
||||
final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
|
||||
if(connectedUser.isPresent()) {
|
||||
if(connectedUser.get().getKey().equals(postToDelete.get().getAuthor().getKey())) {
|
||||
postRepository.delete(postToDelete.get());
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
public void delete(final String pPostKey, final Principal pPrincipal){
|
||||
String userKey = userService.getUserByPrincipal(pPrincipal)
|
||||
.map(User::getKey)
|
||||
.orElseThrow(NoSuchElementException::new);
|
||||
|
||||
Post postToDelete = postRepository.getByKey(pPostKey)
|
||||
.orElseThrow(NoSuchElementException::new);
|
||||
|
||||
if(postToDelete.getAuthor() == null || !postToDelete.getAuthor().getKey().equals(userKey)) {
|
||||
throw new IllegalAccessException();
|
||||
}
|
||||
|
||||
postRepository.delete(postToDelete);
|
||||
}
|
||||
|
||||
public List<Post> search(String pSearchCriteria) {
|
||||
@@ -123,12 +106,12 @@ public class PostService {
|
||||
calculateScore(e, criteriasArray);
|
||||
listSearchEntities.add(e);
|
||||
});
|
||||
Collections.sort(listSearchEntities, (e1, e2) -> e2.getScore() - e1.getScore());
|
||||
listSearchEntities.sort((e1, e2) -> e2.getScore() - e1.getScore());
|
||||
|
||||
return listSearchEntities.stream().map(SearchEntity::getPost).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
void calculateScore(final SearchEntity searchPost, final String[] pCriteriasArray) {
|
||||
private void calculateScore(final SearchEntity searchPost, final String[] pCriteriasArray) {
|
||||
for(final String criteria : pCriteriasArray) {
|
||||
String formattedCriteria = formatForComp(criteria);
|
||||
|
||||
@@ -141,41 +124,41 @@ public class PostService {
|
||||
}
|
||||
}
|
||||
|
||||
String formatForComp(final String pElem) {
|
||||
private String formatForComp(final String pElem) {
|
||||
return StringUtils.stripAccents(pElem.toLowerCase());
|
||||
}
|
||||
|
||||
void calculateScoreForTitle(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
|
||||
private void calculateScoreForTitle(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
if(formatForComp(pSearchPost.getPost().getTitle()).contains(pCriteria)) {
|
||||
pSearchPost.increaseScore(SCORE_TITLE);
|
||||
}
|
||||
}
|
||||
|
||||
void calculateScoreForDescription(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
|
||||
private void calculateScoreForDescription(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
final int criteriaOccurence = StringUtils.countMatches(formatForComp(pSearchPost.getPost().getDescription()),
|
||||
pCriteria);
|
||||
pSearchPost.increaseScore(criteriaOccurence * SCORE_DESCRIPTION);
|
||||
}
|
||||
|
||||
void calculateScoreForText(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
|
||||
private void calculateScoreForText(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
final int criteriaOccurence = StringUtils.countMatches(formatForComp(pSearchPost.getPost().getText()),
|
||||
pCriteria);
|
||||
pSearchPost.increaseScore(criteriaOccurence * SCORE_TEXT);
|
||||
}
|
||||
|
||||
void calculateScoreForCategory(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
|
||||
private void calculateScoreForCategory(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
if(formatForComp(pSearchPost.getPost().getCategory().getName()).contains(pCriteria)) {
|
||||
pSearchPost.increaseScore(SCORE_CATEGORY);
|
||||
}
|
||||
}
|
||||
|
||||
void calculateScoreForAuthorName(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
|
||||
private void calculateScoreForAuthorName(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
if(formatForComp(pSearchPost.getPost().getAuthor().getName()).contains(pCriteria)) {
|
||||
pSearchPost.increaseScore(SCORE_AUTHOR_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
void calculateScoreForAuthorEmail(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
|
||||
private void calculateScoreForAuthorEmail(final SearchEntity pSearchPost, final String pCriteria) {
|
||||
if(formatForComp(pSearchPost.getPost().getAuthor().getEmail()).contains(pCriteria)) {
|
||||
pSearchPost.increaseScore(SCORE_AUTHOR_EMAIL);
|
||||
}
|
||||
|
||||
@@ -13,13 +13,15 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@RequestMapping("/api/versionrevisions")
|
||||
public class VersionRevisionController {
|
||||
private final VersionRevisionRepository repository;
|
||||
private final VersionRepository versionRepository;
|
||||
|
||||
public VersionRevisionController(VersionRevisionRepository repository,
|
||||
VersionRepository versionRepository) {
|
||||
this.repository = repository;
|
||||
this.versionRepository = versionRepository;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private VersionRevisionRepository repository;
|
||||
|
||||
@Autowired
|
||||
private VersionRepository versionRepository;
|
||||
|
||||
@GetMapping("/versions")
|
||||
public Iterable<Version> getVersions() {
|
||||
return versionRepository.findAllOrderByNumber();
|
||||
|
||||
@@ -40,8 +40,7 @@ spring:
|
||||
url: jdbc:postgresql://localhost:5432/codiki
|
||||
username: codiki
|
||||
password: P@ssword
|
||||
# TODO: Delete all Lazy relationships and set following property to false
|
||||
# open-in-view: false
|
||||
open-in-view: false
|
||||
# Because detection is disabled you have to set correct dialect by hand.
|
||||
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
|
||||
# Disable feature detection by this undocumented parameter.
|
||||
|
||||
@@ -3,4 +3,5 @@ INSERT INTO version (number) VALUES ('1.2.0');
|
||||
INSERT INTO version_revision (version_id, text, bugfix) VALUES
|
||||
(4, 'Migration vers Angular 8.2.', FALSE),
|
||||
(4, 'Migration vers Java 11.', FALSE),
|
||||
(4, 'Corrections mineures et améliorations de code.', TRUE);
|
||||
(4, 'Corrections mineures et améliorations de code.', TRUE),
|
||||
(4, 'Ajout d''un gestionnaire de notifications pop-ups.', FALSE);
|
||||
|
||||
@@ -6,6 +6,8 @@ import { catchError } from 'rxjs/operators';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import { NotificationsComponent } from '../notifications/notifications.component';
|
||||
|
||||
const REG_URL_LOGIN = /^https?:\/\/.*(:\d{1,5})?\/api\/account\/login$/;
|
||||
|
||||
@Injectable()
|
||||
export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||
constructor(
|
||||
@@ -18,7 +20,9 @@ export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||
if (err.status === 401) {
|
||||
this.authService.setAnonymous();
|
||||
this.router.navigate(['/login']);
|
||||
window.setTimeout(() => NotificationsComponent.error('Veuillez vous authentifier pour réaliser cette action.'), 500);
|
||||
if (!err.url.match(REG_URL_LOGIN)) {
|
||||
window.setTimeout(() => NotificationsComponent.error('Veuillez vous authentifier pour réaliser cette action.'), 500);
|
||||
}
|
||||
}
|
||||
|
||||
const error = (err.error && err.error.message) || err.statusText;
|
||||
|
||||
@@ -17,8 +17,16 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown" dropdown *ngIf="isAuthenticated()">
|
||||
<a dropdownToggle mdbRippleRadius type="button" class="nav-link dropdown-toggle waves-light" mdbRippleRadius>
|
||||
<i class="fa fa-user-circle"></i> Mon Compte
|
||||
<a dropdownToggle
|
||||
mdbRippleRadius
|
||||
type="button"
|
||||
class="nav-link waves-light"
|
||||
mdbTooltip="Mon compte"
|
||||
placement="bottom"
|
||||
mdbRippleRadius>
|
||||
<img id="user-profile-img"
|
||||
[src]="getConnectedUserImage()"
|
||||
(error)="onUserProfileLoadingError($event)"/>
|
||||
</a>
|
||||
<div *dropdownMenu class="dropdown-menu dropdown-menu-right dropdown dropdown-primary" role="menu">
|
||||
<a class="dropdown-item waves-light" mdbRippleRadius routerLink="/myPosts">
|
||||
@@ -47,7 +55,10 @@
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</a>
|
||||
<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 && category.listSubCategories.length"
|
||||
(click)="openCategoriesLinks(category)">
|
||||
{{category.name}} <i class="fa {{accordionOpenned[category.id] ? 'fa-chevron-down' : 'fa-chevron-right'}} float-right"></i>
|
||||
</a>
|
||||
<div class="categoriesLinks" >
|
||||
|
||||
@@ -27,42 +27,48 @@
|
||||
overflow-x: hidden; /* Disable horizontal scroll */
|
||||
padding-top: 20px; /* Place content 60px from the top */
|
||||
transition: 0.3s; /* 0.5 second transition effect to slide in the sidenav */
|
||||
}
|
||||
|
||||
/* The navigation menu links */
|
||||
.sidenav a {
|
||||
padding: 8px 32px 8px 32px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
display: block;
|
||||
transition: 0.3s;
|
||||
}
|
||||
/* The navigation menu links */
|
||||
a {
|
||||
padding: 8px 32px 8px 32px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
display: block;
|
||||
transition: 0.3s;
|
||||
|
||||
/* When you mouse over the navigation links, change their color */
|
||||
.sidenav a:hover {
|
||||
color: #ccc;
|
||||
background-color: #5c6bc0;
|
||||
}
|
||||
/* When you mouse over the navigation links, change their color */
|
||||
&:hover {
|
||||
color: #ccc;
|
||||
background-color: #5c6bc0;
|
||||
}
|
||||
}
|
||||
|
||||
.sidenav h3 {
|
||||
padding: 8px 8px 8px 32px;
|
||||
color: white;
|
||||
padding-bottom: 25px;
|
||||
border-bottom: 1px solid #5c6bc0;
|
||||
}
|
||||
/* Position and style the close button (top right corner) */
|
||||
.sidenav .closebtn {
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 25px;
|
||||
margin-left: 50px;
|
||||
padding: 8px;
|
||||
h3 {
|
||||
padding: 8px 8px 8px 32px;
|
||||
color: white;
|
||||
padding-bottom: 25px;
|
||||
border-bottom: 1px solid #5c6bc0;
|
||||
}
|
||||
|
||||
/* Position and style the close button (top right corner) */
|
||||
.closebtn {
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 25px;
|
||||
margin-left: 50px;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
|
||||
@media screen and (max-height: 450px) {
|
||||
.sidenav {padding-top: 15px;}
|
||||
.sidenav a {font-size: 18px;}
|
||||
.sidenav {
|
||||
padding-top: 15px;
|
||||
|
||||
a {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#overlay {
|
||||
@@ -74,8 +80,8 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0,0,0,0.5); /* Black background with opacity */
|
||||
z-index: 999; /* Specify a stack order in case you're using a different order for other elements */
|
||||
background-color: rgba(0, 0, 0, 0.5); /* Black background with opacity */
|
||||
z-index: 1031; /* Specify a stack order in case you're using a different order for other elements */
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
@@ -84,6 +90,10 @@
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.2s ease-out;
|
||||
|
||||
a {
|
||||
padding-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item.danger-color-dark:hover {
|
||||
@@ -93,3 +103,15 @@
|
||||
.collapsible i.fa {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
#user-profile-img {
|
||||
filter: opacity(1);
|
||||
transition: filter 0.3s;
|
||||
border-radius: 50%;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
|
||||
&:hover {
|
||||
filter: opacity(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { HeaderService } from './header.service';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
const SIDENAV_WIDTH = '300px';
|
||||
const DEFAULT_USER_ICON = '../../assets/images/default_user_icon.png';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
@@ -80,4 +81,13 @@ export class HeaderComponent implements OnInit {
|
||||
this.accordionOpenned[category.id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
getConnectedUserImage() {
|
||||
const userImage = this.authService.getUser().image;
|
||||
return !!userImage ? `./api/images/loadAvatar/${userImage}` : DEFAULT_USER_ICON;
|
||||
}
|
||||
|
||||
onUserProfileLoadingError(event): void {
|
||||
event.target.src = DEFAULT_USER_ICON;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export class CreateUpdatePostComponent implements OnInit {
|
||||
this.listCategories = [];
|
||||
this.activatedTab = Tabs.EDITION;
|
||||
this.createUpdatePostService.getCategories().subscribe(listCategories => {
|
||||
this.listCategories = listCategories.filter(category => !category.listSubCategories.length);
|
||||
this.listCategories = listCategories.filter(category => !category.listSubCategories);
|
||||
});
|
||||
|
||||
const postKey = this.activatedRoute.snapshot.paramMap.get('postKey');
|
||||
@@ -166,6 +166,7 @@ export class CreateUpdatePostComponent implements OnInit {
|
||||
});
|
||||
} else {
|
||||
this.createUpdatePostService.addPost(this.model).subscribe(post => {
|
||||
NotificationsComponent.success('Article enregistré');
|
||||
this.router.navigate([`/posts/update/${post.key}`]);
|
||||
}, error => {
|
||||
console.log(error);
|
||||
|
||||
@@ -7,5 +7,9 @@
|
||||
<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"
|
||||
mdbTooltip="Créer un article" placement="left">
|
||||
+
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -43,9 +43,6 @@
|
||||
<div class="modal-body">
|
||||
<div class="text-center">
|
||||
<p>Êtes vous sûr de vouloir supprimer cet article ?</p>
|
||||
<p *ngIf="postDeletionFailed" class="red-text">
|
||||
Une erreur est survenue lors de la suppression de l'article.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { NotificationsComponent } from 'src/app/core/notifications/notifications.component';
|
||||
import { Component, OnInit, SecurityContext, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
@@ -38,9 +39,7 @@ export class PostComponent implements OnInit {
|
||||
notFound: boolean;
|
||||
owned: boolean;
|
||||
|
||||
@ViewChild('alertDelete', {static: true}) alertDelete;
|
||||
|
||||
postDeletionFailed: boolean;
|
||||
@ViewChild('alertDelete', {static: false}) alertDelete;
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
@@ -51,7 +50,6 @@ export class PostComponent implements OnInit {
|
||||
) {
|
||||
this.loaded = false;
|
||||
this.owned = false;
|
||||
this.postDeletionFailed = false;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -91,16 +89,13 @@ export class PostComponent implements OnInit {
|
||||
}
|
||||
|
||||
deletePost(): void {
|
||||
this.postDeletionFailed = false;
|
||||
|
||||
this.postService.deletePost(this.post).subscribe(() => {
|
||||
NotificationsComponent.success('Article supprimé.');
|
||||
this.alertDelete.hide();
|
||||
this.router.navigate(['/myPosts']);
|
||||
}, error => {
|
||||
this.postDeletionFailed = true;
|
||||
setTimeout(() => {
|
||||
this.postDeletionFailed = false;
|
||||
}, 3500);
|
||||
console.error(error);
|
||||
NotificationsComponent.error('Une erreur est survenue lors de la suppression de l\'article.');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
BIN
src/main/ts/src/assets/images/default_user_icon.png
Normal file
BIN
src/main/ts/src/assets/images/default_user_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/main/ts/src/assets/images/favicon.ico
Normal file
BIN
src/main/ts/src/assets/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -6,7 +6,7 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<base href="/" />
|
||||
<link rel="icon" type="image/x-icon" href="assets/images/favicon.png" />
|
||||
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
<link href="./assets/css/prism.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user