Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb340a7a78 | |||
| c0fa7c40a6 | |||
| 05a6b48901 | |||
| 8dfdaedafb | |||
| 12363fe362 | |||
| 3daeb789e1 | |||
| 02e0bd4aa2 | |||
| 78dd79f792 | |||
| 16471e9962 | |||
| 6e7ae115e5 | |||
| 95af38bf72 | |||
| 5e5fbd9377 | |||
| 84b1dd6352 | |||
| 1f8a11a73c | |||
| 7f0ff8157f | |||
| 8ec806440b | |||
| 55f8ed986e | |||
| 781eee090f | |||
| e7583a59c2 | |||
| 8205168bce | |||
| 6bbb618f12 | |||
| 6027878622 | |||
| 063d1de074 | |||
| 2f63b798e5 | |||
| dbfa55e0c4 | |||
| aba59dd11f | |||
| 6bd293f08e |
Binary file not shown.
@@ -2,8 +2,8 @@ pipeline {
|
||||
agent any
|
||||
|
||||
tools {
|
||||
maven 'Maven-3.5.0'
|
||||
jdk 'OpenJdk-11.0.2'
|
||||
maven 'Maven-3.6.0'
|
||||
jdk 'OpenJdk-11.0.4'
|
||||
}
|
||||
|
||||
environment {
|
||||
@@ -14,7 +14,7 @@ pipeline {
|
||||
stage('Configuration') {
|
||||
steps {
|
||||
dir('codiki-properties') {
|
||||
git url:'https://gogs.takiguchi.ovh/florian/codiki-properties.git', branch: 'master', credentialsId: 'a6494064-8130-42fd-9d3d-e7734518c79e'
|
||||
git url:'https://gitea.takiguchi.ovh/Codiki/codiki-properties.git', branch: 'master', credentialsId: 'Jenkins-gitea'
|
||||
}
|
||||
script {
|
||||
sh 'cp ./codiki-properties/application-prod.yml ./src/main/resources/application.yml'
|
||||
@@ -88,9 +88,9 @@ pipeline {
|
||||
cleanRemote: false,
|
||||
excludes: '',
|
||||
execCommand: """
|
||||
mv /opt/codiki-2/codiki.jar /opt/codiki-2/bin/codiki.jar
|
||||
sudo /opt/codiki-2/bin/codiki-jenkins.sh
|
||||
sudo service codiki-2 restart
|
||||
mv /home/codiki/codiki.jar /opt/codiki/bin/codiki.jar
|
||||
chmod 700 /opt/codiki/bin/codiki.jar
|
||||
sudo service codiki restart
|
||||
""",
|
||||
execTimeout: 120000,
|
||||
flatten: false,
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.codiki</groupId>
|
||||
<artifactId>codiki</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>1.2.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>codiki</name>
|
||||
|
||||
@@ -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 {
|
||||
private final CategoryRepository categoryRepository;
|
||||
|
||||
@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;
|
||||
/** 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.codiki.core.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
public class RobotsTxtController {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RobotsTxtController.class);
|
||||
|
||||
@RequestMapping(value = "/robots.txt")
|
||||
public void robots(HttpServletResponse response) {
|
||||
try {
|
||||
response.getWriter().write("User-agent: *\nDisallow: /\n");
|
||||
} catch (IOException ex) {
|
||||
LOG.info("Error during robots.txt serving.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/* ******************* */
|
||||
@@ -45,6 +47,7 @@ public class Category implements Serializable {
|
||||
@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)}
|
||||
@@ -23,6 +21,6 @@ public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -45,18 +41,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
// Permits all
|
||||
.antMatchers(
|
||||
"/api/account/login",
|
||||
"/api/account/logout",
|
||||
"/api/account/signin"
|
||||
"/api/account/signin",
|
||||
"/robots.txt"
|
||||
).permitAll()
|
||||
.antMatchers(
|
||||
"/api/images/uploadAvatar",
|
||||
"/api/images/myImages",
|
||||
"/api/posts/myPosts",
|
||||
"/api/account/changePassword",
|
||||
"/api/account/"
|
||||
).authenticated()
|
||||
.antMatchers(
|
||||
HttpMethod.GET,
|
||||
"/api/categories",
|
||||
@@ -66,6 +57,15 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
"/api/images/**",
|
||||
"/api/posts/**"
|
||||
).permitAll()
|
||||
.antMatchers(
|
||||
"/api/images/uploadAvatar",
|
||||
"/api/images/myImages",
|
||||
"/api/account/changePassword",
|
||||
"/api/account/",
|
||||
"/api/posts/myPosts",
|
||||
"/api/posts/preview",
|
||||
"/api/posts/"
|
||||
).authenticated()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
// Allow to avoid login form at authentication failure from Angular app
|
||||
|
||||
@@ -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,6 +9,12 @@ 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. */
|
||||
@@ -25,12 +22,17 @@ public class FileUploadService {
|
||||
/** 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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,15 +17,18 @@ import javax.swing.text.html.Option;
|
||||
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,
|
||||
public void update(@RequestBody final Post pPost,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
|
||||
postService.update(pPost, pRequest, pResponse, pPrincipal);
|
||||
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;
|
||||
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;
|
||||
|
||||
static final int SCORE_DESCRIPTION = 5;
|
||||
private final PostRepository postRepository;
|
||||
|
||||
static final int SCORE_TEXT = 1;
|
||||
private final CategoryRepository categoryRepository;
|
||||
|
||||
static final int SCORE_CATEGORY = 30;
|
||||
private final UserService userService;
|
||||
|
||||
static final int SCORE_AUTHOR_NAME = 40;
|
||||
|
||||
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);
|
||||
/** Constructor. */
|
||||
public PostService(PostRepository postRepository,
|
||||
CategoryRepository categoryRepository,
|
||||
UserService userService) {
|
||||
this.postRepository = postRepository;
|
||||
this.categoryRepository = categoryRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
return result;
|
||||
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,12 +13,14 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@RequestMapping("/api/versionrevisions")
|
||||
public class VersionRevisionController {
|
||||
private final VersionRevisionRepository repository;
|
||||
private final VersionRepository versionRepository;
|
||||
|
||||
@Autowired
|
||||
private VersionRevisionRepository repository;
|
||||
|
||||
@Autowired
|
||||
private VersionRepository versionRepository;
|
||||
public VersionRevisionController(VersionRevisionRepository repository,
|
||||
VersionRepository versionRepository) {
|
||||
this.repository = repository;
|
||||
this.versionRepository = versionRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/versions")
|
||||
public Iterable<Version> getVersions() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
app:
|
||||
name: Codiki
|
||||
description: A wiki application.
|
||||
version: 1.2.0
|
||||
version: 1.2.2
|
||||
platform: develop
|
||||
|
||||
codiki:
|
||||
@@ -21,6 +21,9 @@ logging:
|
||||
|
||||
server:
|
||||
# use-forward-headers=true
|
||||
error:
|
||||
whitelabel:
|
||||
enabled: false # Disable html error responses.
|
||||
port: 8080
|
||||
# ssl:
|
||||
# key-store: /home/takiguchi/Developpement/Java/codiki/keystore.p12
|
||||
@@ -37,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);
|
||||
|
||||
5
src/main/sql/update_1.2.1.sql
Executable file
5
src/main/sql/update_1.2.1.sql
Executable file
@@ -0,0 +1,5 @@
|
||||
INSERT INTO version (number) VALUES ('1.2.1');
|
||||
|
||||
INSERT INTO version_revision (version_id, text, bugfix) VALUES
|
||||
(5, 'Nettoyage de code.', TRUE),
|
||||
(5, 'Modifications visuelles, notamment sur l''image de l''entête du site et le menu principal.', FALSE);
|
||||
12
src/main/sql/update_1.2.2.sql
Normal file
12
src/main/sql/update_1.2.2.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
INSERT INTO category (name, creator_id) VALUES
|
||||
('Windows', 1),
|
||||
('Outillage', 1);
|
||||
|
||||
INSERT INTO sub_category VALUES
|
||||
(12, 2),
|
||||
(13, 4);
|
||||
|
||||
INSERT INTO version (number) VALUES ('1.2.2');
|
||||
|
||||
INSERT INTO version_revision (version_id, text, bugfix) VALUES
|
||||
(6, 'Ajout des catégories "Windows" et "Outillage".', FALSE);
|
||||
@@ -39,9 +39,9 @@
|
||||
<a routerLink="/accountSettings" class="indigo-text">
|
||||
Annuler
|
||||
</a>
|
||||
<button class="float-right waves-effect waves-light indigo btn"
|
||||
<button class="float-right waves-effect waves-light indigo white-text btn"
|
||||
type="submit" [disabled]="!profilEditionForm.form.valid">
|
||||
Suivant
|
||||
Enregistrer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -80,7 +80,7 @@ export class ProfilEditionComponent implements OnInit {
|
||||
|
||||
onSubmit(): void {
|
||||
this.profilEditionService.updateUser(this.model).subscribe(() => {
|
||||
NotificationsComponent.success('Modification enregistrée.');
|
||||
NotificationsComponent.success('Modifications enregistrées.');
|
||||
}, error => {
|
||||
NotificationsComponent.error('L\'adresse mail saisie n\'est pas disponible.');
|
||||
});
|
||||
|
||||
@@ -4,6 +4,9 @@ import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/c
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
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 {
|
||||
@@ -17,6 +20,9 @@ export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||
if (err.status === 401) {
|
||||
this.authService.setAnonymous();
|
||||
this.router.navigate(['/login']);
|
||||
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;
|
||||
|
||||
27
src/main/ts/src/app/core/post-card/post-card.component.html
Normal file
27
src/main/ts/src/app/core/post-card/post-card.component.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="post-card hoverable" routerLink="/posts/{{post.key}}">
|
||||
<mdb-card>
|
||||
<!-- Picture -->
|
||||
<mdb-card-img [src]="post.image" alt="Article"></mdb-card-img>
|
||||
<!-- Body -->
|
||||
<mdb-card-body>
|
||||
<!-- Title -->
|
||||
<mdb-card-title>
|
||||
<h4>{{post.title}}</h4>
|
||||
</mdb-card-title>
|
||||
<!-- Description -->
|
||||
<mdb-card-text>
|
||||
{{post.description}}
|
||||
</mdb-card-text>
|
||||
</mdb-card-body>
|
||||
<!-- Footer -->
|
||||
<div class="card-footer text-muted mt-4">
|
||||
<img [src]="getAvatarUrl()"
|
||||
class="author-img"
|
||||
[alt]="post.author.name"
|
||||
[mdbTooltip]="post.author.name"
|
||||
placement="bottom"/>
|
||||
Article écrit par {{post.author.name}}
|
||||
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
|
||||
</div>
|
||||
</mdb-card>
|
||||
</div>
|
||||
@@ -3,35 +3,11 @@ import { Post } from '../entities';
|
||||
|
||||
@Component({
|
||||
selector: 'app-post-card',
|
||||
template: `
|
||||
<div class="card hoverable">
|
||||
<div class="view hm-white-slight waves-light" mdbRippleRadius>
|
||||
<img id="post-image" class="img-fluid" [src]="post.image" alt="Article" />
|
||||
<a routerLink="/posts/{{post.key}}">
|
||||
<div class="mask"></div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">{{post.title}}</h4>
|
||||
<p class="card-text">{{post.description}}</p>
|
||||
</div>
|
||||
<div class="card-data">
|
||||
<img [src]="getAvatarUrl()"
|
||||
class="author-img"
|
||||
[alt]="post.author.name"
|
||||
[mdbTooltip]="post.author.name"
|
||||
placement="bottom"/>
|
||||
Article écrit par {{post.author.name}}
|
||||
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
|
||||
</div>
|
||||
</div>`,
|
||||
templateUrl: './post-card.component.html',
|
||||
styles: [`
|
||||
div.card {
|
||||
.post-card {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.card .card-data {
|
||||
padding: 15px;
|
||||
background-color: #f5f5f5;
|
||||
cursor: pointer;
|
||||
}
|
||||
.creation-date-area {
|
||||
color: #bdbdbd;
|
||||
|
||||
@@ -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 {
|
||||
/* 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 {
|
||||
/* When you mouse over the navigation links, change their color */
|
||||
&:hover {
|
||||
color: #ccc;
|
||||
background-color: #5c6bc0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidenav h3 {
|
||||
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 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');
|
||||
@@ -159,11 +159,18 @@ export class CreateUpdatePostComponent implements OnInit {
|
||||
|
||||
if (this.model.key) {
|
||||
this.createUpdatePostService.updatePost(this.model).subscribe(post => {
|
||||
NotificationsComponent.error('Modification enregistrée');
|
||||
NotificationsComponent.success('Modification enregistrée');
|
||||
}, error => {
|
||||
console.log(error);
|
||||
NotificationsComponent.error('Une erreur est survenue lors de l\'enregistrement des modifications.');
|
||||
});
|
||||
} else {
|
||||
this.createUpdatePostService.addPost(this.model).subscribe(post => {
|
||||
NotificationsComponent.success('Article enregistré');
|
||||
this.router.navigate([`/posts/update/${post.key}`]);
|
||||
}, error => {
|
||||
console.log(error);
|
||||
NotificationsComponent.error('Une erreur est survenue lors de la création du post.');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -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.');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/main/ts/src/assets/doc/codiki_user_manual.pdf
Normal file
BIN
src/main/ts/src/assets/doc/codiki_user_manual.pdf
Normal file
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 |
@@ -1,5 +1,5 @@
|
||||
export const environment = {
|
||||
production: false,
|
||||
appVersion: '1.2.0',
|
||||
appVersion: '1.2.2',
|
||||
title: 'Intégration'
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
appVersion: '1.1.0',
|
||||
appVersion: '1.2.2',
|
||||
title: ''
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
appVersion: '1.2.0',
|
||||
appVersion: '1.2.2',
|
||||
title: 'Développement'
|
||||
};
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
<head>
|
||||
<title>Codiki</title>
|
||||
<meta charset="utf-8"/>
|
||||
<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>
|
||||
|
||||
@@ -74,3 +74,7 @@ body {
|
||||
border: 1px solid rgba(0, 0, 0, 0.125) !important;
|
||||
}
|
||||
/* ***** End of Card style ***** */
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
330
src/test/java/org/codiki/core/services/ParserServiceTest.java
Normal file
330
src/test/java/org/codiki/core/services/ParserServiceTest.java
Normal file
@@ -0,0 +1,330 @@
|
||||
package org.codiki.core.services;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ParserServiceTest {
|
||||
|
||||
private ParserService parserService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
parserService = new ParserService();
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
/* H E A D E R S P A R S I N G */
|
||||
/* *********************************************************** */
|
||||
|
||||
@Test
|
||||
public void testParseHeaders_h1() {
|
||||
final String strInit = "[h1]Header[/h1]";
|
||||
final String strExpected = "<h1>Header</h1>";
|
||||
|
||||
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeaders_h2() {
|
||||
final String strInit = "[h2]Header[/h2]";
|
||||
final String strExpected = "<h2>Header</h2>";
|
||||
|
||||
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeaders_h3() {
|
||||
final String strInit = "[h3]Header[/h3]";
|
||||
final String strExpected = "<h3>Header</h3>";
|
||||
|
||||
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeader_onlyCoupleOfTags() {
|
||||
final String strInit = "[h1][h1]Test[/h1]";
|
||||
final String strExpected = "[h1]<h1>Test</h1>";
|
||||
|
||||
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeader_multipleCouples() {
|
||||
final String strInit = "[h1]Test1[/h1] [h1]Test2[/h1]";
|
||||
final String strExpected = "<h1>Test1</h1> <h1>Test2</h1>";
|
||||
|
||||
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeaders() {
|
||||
final String strInit = "[h1]Header1[/h1]\n" +
|
||||
" Some text in header1\n" +
|
||||
" [h2]Header2[/h2]\n" +
|
||||
" Some text in header2\n" +
|
||||
" [h3]Header3[/h3]\n" +
|
||||
" Some text in header3\n" +
|
||||
" [h3][h3]Header3[/h3]\n" +
|
||||
" Some text in header3\n" +
|
||||
" [h2]Header2[/h2]\n" +
|
||||
" Some text in header2\n" +
|
||||
" [h3]Header3[/h3][/h3]\n" +
|
||||
" Some text in header3\n" +
|
||||
" [h1]Header1[/h1]\n" +
|
||||
" Some text in header1";
|
||||
|
||||
final String strExpected = "<h1>Header1</h1>\n" +
|
||||
" Some text in header1\n" +
|
||||
" <h2>Header2</h2>\n" +
|
||||
" Some text in header2\n" +
|
||||
" <h3>Header3</h3>\n" +
|
||||
" Some text in header3\n" +
|
||||
" [h3]<h3>Header3</h3>\n" +
|
||||
" Some text in header3\n" +
|
||||
" <h2>Header2</h2>\n" +
|
||||
" Some text in header2\n" +
|
||||
" <h3>Header3[/h3]</h3>\n" +
|
||||
" Some text in header3\n" +
|
||||
" <h1>Header1</h1>\n" +
|
||||
" Some text in header1";
|
||||
|
||||
assertThat(parserService.parseHeaders(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
/* B A C K S P A C E S P A R S I N G */
|
||||
/* *********************************************************** */
|
||||
|
||||
@Test
|
||||
public void testParseBackSpaces() {
|
||||
final String strInit = "Hello\nworld!";
|
||||
final String strExpected = "Hello<br/>world!";
|
||||
|
||||
assertThat(parserService.parseBackSpaces(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBackSpaces_endTagCode() {
|
||||
final String strInit = "[/code]\n\n";
|
||||
final String strExpected = "[/code]\n";
|
||||
|
||||
assertThat(parserService.parseBackSpaces(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBackSpaces_endTagCode_2() {
|
||||
final String strInit = "[/code]\n";
|
||||
final String strExpected = "[/code]<br/>";
|
||||
|
||||
assertThat(parserService.parseBackSpaces(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
/* I M A G E S P A R S I N G */
|
||||
/* *********************************************************** */
|
||||
|
||||
@Test
|
||||
public void testParseImages_simpleImg() {
|
||||
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage\" /]");
|
||||
final String strExpected = "<img src=\"pathToImage\" alt=\"\" />";
|
||||
|
||||
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseImages_subTagAlt() {
|
||||
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage\" alt=\"alternative\" /]");
|
||||
final String strExpected = "<img src=\"pathToImage\" alt=\"alternative\" />";
|
||||
|
||||
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseImages_multipleImages() {
|
||||
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage1\" /]\n" +
|
||||
" [img src=\"pathToImage2\" /]");
|
||||
final String strExpected = "<img src=\"pathToImage1\" alt=\"\" />\n" +
|
||||
" <img src=\"pathToImage2\" alt=\"\" />";
|
||||
|
||||
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseImages() {
|
||||
final String strInit = StringEscapeUtils.escapeHtml("[img src=\"pathToImage1\" /]\n" +
|
||||
" [img src=\"pathToImage2\" alt=\"alternativeOfImage2\" /]\n" +
|
||||
" [img src=\"pathToImage3\" lgd=\"legendOfImage3\" /]\n" +
|
||||
" [img src=\"pathToImage4\" alt=\"alternativeOfImage4\" lgd=\"legendOfImage4\" /]\n" +
|
||||
" [img src=\"pathToImage5\" \" /]\n" +
|
||||
" [img src=\"pathToImage6\" src=\"pathToImage6_2\" /]\n" +
|
||||
" [img src=\"pathToImage7\" alt=\"alternativeOfImage7\" alt=\"alternativeOfImage7_2\" /]\n" +
|
||||
" [img src=\"pathToImage8\" alt=\"legendOfImage8\" alt=\"legendOfImage8_2\" /]\n" +
|
||||
" [img alt=\"alternativeOfImage9\" src=\"pathToImage9\" lgd=\"legendOfImage9\" /]\n" +
|
||||
" [img lgd=\"legendOfImage10\" src=\"pathToImage10\" alt=\"alternativeOfImage10\" /]\n" +
|
||||
" [img lgd=\"legendOfImage11\" alt=\"alternativeOfImage11\" src=\"pathToImage11\" /]");
|
||||
|
||||
final String strExpected = "<img src=\"pathToImage1\" alt=\"\" />\n" +
|
||||
" <img src=\"pathToImage2\" alt=\"alternativeOfImage2\" />\n" +
|
||||
" [img src="pathToImage3" lgd="legendOfImage3" /]\n" +
|
||||
" [img src="pathToImage4" alt="alternativeOfImage4" lgd="legendOfImage4" /]\n" +
|
||||
" [img src="pathToImage5" " /]\n" +
|
||||
" [img src="pathToImage6" src="pathToImage6_2" /]\n" +
|
||||
" [img src="pathToImage7" alt="alternativeOfImage7" alt="alternativeOfImage7_2" /]\n" +
|
||||
" [img src="pathToImage8" alt="legendOfImage8" alt="legendOfImage8_2" /]\n" +
|
||||
" [img alt="alternativeOfImage9" src="pathToImage9" lgd="legendOfImage9" /]\n" +
|
||||
" [img lgd="legendOfImage10" src="pathToImage10" alt="alternativeOfImage10" /]\n" +
|
||||
" [img lgd="legendOfImage11" alt="alternativeOfImage11" src="pathToImage11" /]";
|
||||
|
||||
assertThat(parserService.parseImages(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
/* L I N K S P A R S I N G */
|
||||
/* *********************************************************** */
|
||||
|
||||
@Test
|
||||
public void testParseLinks_simpleLink() {
|
||||
final String strInit = StringEscapeUtils.escapeHtml("[link href=\"pathToLink\" txt=\"textOfLink\" /]");
|
||||
final String strExpected = "<a href=\"pathToLink\">textOfLink</a>";
|
||||
|
||||
assertThat(parserService.parseLinks(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLinks_multipleLinks() {
|
||||
final String strInit = StringEscapeUtils.escapeHtml("[link href=\"pathToLink1\" txt=\"textOfLink1\" /]\n" +
|
||||
" [link href=\"pathToLink2\" txt=\"textOfLink2\" /]");
|
||||
final String strExpected = "<a href=\"pathToLink1\">textOfLink1</a>\n" +
|
||||
" <a href=\"pathToLink2\">textOfLink2</a>";
|
||||
|
||||
assertThat(parserService.parseLinks(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
/* C O D E P A R S I N G */
|
||||
/* *********************************************************** */
|
||||
|
||||
@Test
|
||||
public void testParseCode_simpleCode() {
|
||||
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
|
||||
"def foo():\n" +
|
||||
" return \"Hello world!\"\n" +
|
||||
"[/code]\n" +
|
||||
"\n"));
|
||||
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return "Hello world!"\n</code></pre>";
|
||||
|
||||
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCode_multipleCodes() {
|
||||
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
|
||||
"def foo():\n" +
|
||||
" return \"Hello world!\"\n" +
|
||||
"[/code]\n" +
|
||||
"\n" +
|
||||
"[code lg=\"java\"]\n" +
|
||||
"public static void main(final String... pArgs) {\n" +
|
||||
" System.out.println(\"Hello world!\");\n" +
|
||||
"}\n" +
|
||||
"[/code]\n" +
|
||||
"\n"));
|
||||
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return "Hello world!"\n</code></pre><pre class=\"line-numbers\"><code class=\"language-java\">public static void main(final String... pArgs) {\n System.out.println("Hello world!");\n}\n</code></pre>";
|
||||
|
||||
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLinks_backSlashAndCodeParsing() {
|
||||
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
|
||||
"def foo():\n" +
|
||||
" return \"Hello world!\"\n" +
|
||||
"[/code]\n" +
|
||||
"\n"));
|
||||
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return "Hello world!"\n</code></pre>";
|
||||
|
||||
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLinks_backSlashAndCodeParsing_multiple() {
|
||||
final String strInit = parserService.parseBackSpaces(StringEscapeUtils.escapeHtml("[code lg=\"python\"]\n" +
|
||||
"def foo():\n" +
|
||||
" return \"Hello world!\"\n" +
|
||||
"[/code]\n" +
|
||||
"\n" +
|
||||
"[code lg=\"java\"]\n" +
|
||||
"public static void main(final String... pArgs) {\n" +
|
||||
" System.out.println(\"Hello world!\");\n" +
|
||||
"}\n" +
|
||||
"[/code]\n" +
|
||||
"\n"));
|
||||
final String strExpected = "<pre class=\"line-numbers\"><code class=\"language-python\">def foo():\n return "Hello world!"\n</code></pre><pre class=\"line-numbers\"><code class=\"language-java\">public static void main(final String... pArgs) {\n System.out.println("Hello world!");\n}\n</code></pre>";
|
||||
|
||||
assertThat(parserService.parseCode(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
final String strInit = "\n" +
|
||||
"[h1]Title 1[/h1]\n" +
|
||||
"[h2]Title 2[/h2]\n" +
|
||||
"[h3]Title 3[/h3]\n" +
|
||||
"Some text on multiple\n" +
|
||||
"lines\n" +
|
||||
"like\n" +
|
||||
"here!\n" +
|
||||
"\n" +
|
||||
"[img src=\"pathToImage\" alt=\"alternativeOfImage\" /]\n" +
|
||||
"\n" +
|
||||
"[code lg=\"java\"]\n" +
|
||||
"public static void main(final String... pArgs) {\n" +
|
||||
" System.out.println(\"Hello world!\");\n" +
|
||||
"}\n" +
|
||||
"[/code]\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
"[link href=\"pathToLink1\" txt=\"textOfLink1\" /]\n" +
|
||||
"\n" +
|
||||
"[img src=\"pathToImage2\" alt=\"alternativeOfImage2\" lgd=\"legendOfImage2\" /]\n" +
|
||||
"[link href=\"pathToLink2\" txt=\"textOfLink2\" /]\n" +
|
||||
"\n" +
|
||||
"[code lg=\"python\"]\n" +
|
||||
"def foo():\n" +
|
||||
" return \"Hello world!\"\n" +
|
||||
"[/code]\n" +
|
||||
"\n" +
|
||||
"";
|
||||
|
||||
final String strExpected = "<br/>" +
|
||||
"<h1>Title 1</h1><br/>" +
|
||||
"<h2>Title 2</h2><br/>" +
|
||||
"<h3>Title 3</h3><br/>" +
|
||||
"Some text on multiple<br/>" +
|
||||
"lines<br/>" +
|
||||
"like<br/>" +
|
||||
"here!<br/>" +
|
||||
"<br/>" +
|
||||
"<img src=\"pathToImage\" alt=\"alternativeOfImage\" /><br/>" +
|
||||
"<br/>" +
|
||||
"<pre class=\"line-numbers\"><code class=\"language-java\">" +
|
||||
"public static void main(final String... pArgs) {\n" +
|
||||
" System.out.println("Hello world!");\n" +
|
||||
"}\n" +
|
||||
"</code></pre><br/>" +
|
||||
"<a href=\"pathToLink1\">textOfLink1</a><br/>" +
|
||||
"<br/>" +
|
||||
"[img src="pathToImage2" alt="alternativeOfImage2" lgd="legendOfImage2" /]<br/>" +
|
||||
"<a href=\"pathToLink2\">textOfLink2</a><br/>" +
|
||||
"<br/>" +
|
||||
"<pre class=\"line-numbers\"><code class=\"language-python\">" +
|
||||
"def foo():\n" +
|
||||
" return "Hello world!"\n" +
|
||||
"</code></pre>";
|
||||
|
||||
assertThat(parserService.parse(strInit)).isEqualTo(strExpected);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user