diff --git a/pom.xml b/pom.xml
index c83d1a2..6080791 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,6 +59,11 @@
postgresql
runtime
+
+ commons-lang
+ commons-lang
+ 2.3
+
diff --git a/src/main/java/org/codiki/login/LoginController.java b/src/main/java/org/codiki/account/AccountController.java
similarity index 53%
rename from src/main/java/org/codiki/login/LoginController.java
rename to src/main/java/org/codiki/account/AccountController.java
index 2fd0f9e..c217811 100644
--- a/src/main/java/org/codiki/login/LoginController.java
+++ b/src/main/java/org/codiki/account/AccountController.java
@@ -1,32 +1,36 @@
-package org.codiki.login;
+package org.codiki.account;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.codiki.core.entities.dto.UserDAO;
+import org.codiki.core.entities.dto.PasswordWrapperDTO;
+import org.codiki.core.entities.dto.UserDTO;
+import org.codiki.core.entities.persistence.User;
import org.codiki.core.security.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/account")
-public class LoginController {
+public class AccountController {
private static final String HEADER_TOKEN = "token";
@Autowired
- private TokenService tokenService;
-
- @Autowired
- private LoginService loginService;
+ private AccountService accountService;
+ @Autowired
+ private TokenService tokenService;
+
+
@PostMapping("/login")
- public UserDAO login(@RequestBody UserDAO pUser, HttpServletResponse response) {
- return loginService.checkCredentials(response, pUser);
+ public UserDTO login(@RequestBody UserDTO pUser, HttpServletResponse response) {
+ return accountService.checkCredentials(response, pUser);
}
@GetMapping("/logout")
@@ -34,4 +38,10 @@ public class LoginController {
tokenService.removeUser(pRequest.getHeader(HEADER_TOKEN));
}
+ @PutMapping("/changePassword")
+ public boolean changePassword(@RequestBody final PasswordWrapperDTO pPasswordWrapper,
+ final HttpServletRequest pRequest,
+ final HttpServletResponse pResponse) {
+ return accountService.changePassword(tokenService.getAuthenticatedUserByToken(pRequest), pPasswordWrapper, pResponse);
+ }
}
diff --git a/src/main/java/org/codiki/account/AccountService.java b/src/main/java/org/codiki/account/AccountService.java
new file mode 100644
index 0000000..eaf3760
--- /dev/null
+++ b/src/main/java/org/codiki/account/AccountService.java
@@ -0,0 +1,69 @@
+package org.codiki.account;
+
+import java.util.Optional;
+
+import javax.naming.AuthenticationException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.codiki.core.entities.dto.PasswordWrapperDTO;
+import org.codiki.core.entities.dto.UserDTO;
+import org.codiki.core.entities.persistence.User;
+import org.codiki.core.repositories.UserRepository;
+import org.codiki.core.security.TokenService;
+import org.codiki.core.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AccountService {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Autowired
+ private TokenService tokenService;
+
+ /**
+ * Check the user credentials and generate him a token if they are correct.
+ *
+ * @param pUser
+ * The user sent from client.
+ * @return The user populated with the generated token.
+ * @throws AuthenticationException
+ * If the credentials are wrong.
+ */
+ public UserDTO checkCredentials(HttpServletResponse pResponse, UserDTO pUser) {
+ UserDTO result = null;
+
+ Optional user = userRepository.findByEmail(pUser.getEmail());
+
+ if(user.isPresent() && StringUtils.compareHash(pUser.getPassword(), user.get().getPassword())) {
+ tokenService.addUser(user.get());
+ result = new UserDTO(user.get(), true);
+ } else {
+ pResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ }
+
+ return result;
+ }
+
+ public boolean changePassword(final User pUser, final PasswordWrapperDTO pPasswordWrapper,
+ final HttpServletResponse pResponse) {
+ boolean result = false;
+
+ if(pPasswordWrapper.getNewPassword().equals(pPasswordWrapper.getConfirmPassword())) {
+ // We fetch the connected user from database to get his hashed password
+ final Optional userFromDb = userRepository.findById(pUser.getId());
+ if(userFromDb.isPresent() && StringUtils.compareHash(pPasswordWrapper.getOldPassword(),
+ userFromDb.get().getPassword())) {
+ result = true;
+ userFromDb.get().setPassword(StringUtils.hashPassword(pPasswordWrapper.getNewPassword()));
+ userRepository.save(userFromDb.get());
+ } else {
+ pResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/codiki/categories/CategoryController.java b/src/main/java/org/codiki/categories/CategoryController.java
new file mode 100644
index 0000000..719735c
--- /dev/null
+++ b/src/main/java/org/codiki/categories/CategoryController.java
@@ -0,0 +1,35 @@
+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 org.codiki.core.entities.persistence.Category;
+import org.codiki.core.repositories.CategoryRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/categories")
+public class CategoryController {
+
+ @Autowired
+ private CategoryRepository categoryRepository;
+
+ @GetMapping("/{id}")
+ public CategoryDTO findById(@PathVariable("id") final Long pId) {
+ final Optional result = categoryRepository.findById(pId);
+ return result.isPresent() ? new CategoryDTO(result.get()) : null;
+ }
+
+ @GetMapping("/")
+ public List getAll() {
+ return StreamSupport.stream(categoryRepository.findAll().spliterator(), false)
+ .map(CategoryDTO::new).collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/org/codiki/core/AbstractFilter.java b/src/main/java/org/codiki/core/AbstractFilter.java
index cdbe8c3..ae361fc 100644
--- a/src/main/java/org/codiki/core/AbstractFilter.java
+++ b/src/main/java/org/codiki/core/AbstractFilter.java
@@ -110,10 +110,12 @@ public abstract class AbstractFilter implements Filter {
boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) {
boolean result = false;
- for(final HttpMethod routeMethod : pRoute.getMethod().get()) {
- if(routeMethod.name().equals(pRequestMethod)) {
- result = true;
- break;
+ if(pRoute.getMethod().isPresent()) {
+ for(final HttpMethod routeMethod : pRoute.getMethod().get()) {
+ if(routeMethod.name().equals(pRequestMethod)) {
+ result = true;
+ break;
+ }
}
}
diff --git a/src/main/java/org/codiki/core/entities/dto/CategoryDTO.java b/src/main/java/org/codiki/core/entities/dto/CategoryDTO.java
new file mode 100644
index 0000000..6509298
--- /dev/null
+++ b/src/main/java/org/codiki/core/entities/dto/CategoryDTO.java
@@ -0,0 +1,36 @@
+package org.codiki.core.entities.dto;
+
+import org.codiki.core.entities.persistence.Category;
+
+public class CategoryDTO {
+ private Long id;
+
+ private String name;
+
+ public CategoryDTO() {
+ super();
+ }
+
+ public CategoryDTO(final Category pCategory) {
+ this();
+ id = pCategory.getId();
+ name = pCategory.getName();
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/src/main/java/org/codiki/core/entities/dto/PasswordWrapperDTO.java b/src/main/java/org/codiki/core/entities/dto/PasswordWrapperDTO.java
new file mode 100644
index 0000000..4f95629
--- /dev/null
+++ b/src/main/java/org/codiki/core/entities/dto/PasswordWrapperDTO.java
@@ -0,0 +1,34 @@
+package org.codiki.core.entities.dto;
+
+public class PasswordWrapperDTO {
+
+ private String oldPassword;
+
+ private String newPassword;
+
+ private String confirmPassword;
+
+ public String getOldPassword() {
+ return oldPassword;
+ }
+
+ public void setOldPassword(String oldPassword) {
+ this.oldPassword = oldPassword;
+ }
+
+ public String getNewPassword() {
+ return newPassword;
+ }
+
+ public void setNewPassword(String newPassword) {
+ this.newPassword = newPassword;
+ }
+
+ public String getConfirmPassword() {
+ return confirmPassword;
+ }
+
+ public void setConfirmPassword(String confirmPassword) {
+ this.confirmPassword = confirmPassword;
+ }
+}
diff --git a/src/main/java/org/codiki/core/entities/dto/PostDTO.java b/src/main/java/org/codiki/core/entities/dto/PostDTO.java
new file mode 100644
index 0000000..ef8473c
--- /dev/null
+++ b/src/main/java/org/codiki/core/entities/dto/PostDTO.java
@@ -0,0 +1,105 @@
+package org.codiki.core.entities.dto;
+
+import java.util.Date;
+
+import org.codiki.core.entities.persistence.Post;
+
+public class PostDTO {
+
+ private String key;
+
+ private String title;
+
+ private String text;
+
+ private String description;
+
+ private String image;
+
+ private Date creationDate;
+
+ private UserDTO author;
+
+ private CategoryDTO category;
+
+ public PostDTO() {
+ super();
+ }
+
+ public PostDTO(final Post pPost) {
+ this();
+ key = pPost.getKey();
+ title = pPost.getTitle();
+ text = pPost.getText();
+ description = pPost.getDescription();
+ image = pPost.getImage();
+ creationDate = pPost.getCreationDate();
+ author = new UserDTO(pPost.getAuthor());
+ category = new CategoryDTO(pPost.getCategory());
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getImage() {
+ return image;
+ }
+
+ public void setImage(String image) {
+ this.image = image;
+ }
+
+ public Date getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Date creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public UserDTO getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(UserDTO author) {
+ this.author = author;
+ }
+
+ public CategoryDTO getCategory() {
+ return category;
+ }
+
+ public void setCategory(CategoryDTO category) {
+ this.category = category;
+ }
+
+}
diff --git a/src/main/java/org/codiki/core/entities/dto/UserDAO.java b/src/main/java/org/codiki/core/entities/dto/UserDTO.java
similarity index 87%
rename from src/main/java/org/codiki/core/entities/dto/UserDAO.java
rename to src/main/java/org/codiki/core/entities/dto/UserDTO.java
index 724c63d..ced140a 100644
--- a/src/main/java/org/codiki/core/entities/dto/UserDAO.java
+++ b/src/main/java/org/codiki/core/entities/dto/UserDTO.java
@@ -5,7 +5,7 @@ import java.util.Date;
import org.codiki.core.entities.persistence.Role;
import org.codiki.core.entities.persistence.User;
-public class UserDAO {
+public class UserDTO {
private String key;
@@ -23,18 +23,24 @@ public class UserDAO {
private String token;
- public UserDAO() {
+ public UserDTO() {
super();
}
- public UserDAO(final User pUser) {
+ public UserDTO(final User pUser) {
key = pUser.getKey();
name = pUser.getName();
email = pUser.getEmail();
image = pUser.getImage();
inscriptionDate = pUser.getInscriptionDate();
role = pUser.getRole();
- token = pUser.getToken().getValue();
+ }
+
+ public UserDTO(final User pUser, final boolean pWithToken) {
+ this(pUser);
+ if(pWithToken) {
+ token = pUser.getToken().getValue();
+ }
}
public String getKey() {
diff --git a/src/main/java/org/codiki/core/entities/persistence/Category.java b/src/main/java/org/codiki/core/entities/persistence/Category.java
index 25de86a..17f4ea7 100644
--- a/src/main/java/org/codiki/core/entities/persistence/Category.java
+++ b/src/main/java/org/codiki/core/entities/persistence/Category.java
@@ -14,6 +14,8 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
+import org.codiki.core.entities.dto.CategoryDTO;
+
@Entity
@Table(name="category")
@Inheritance(strategy = InheritanceType.JOINED)
@@ -42,6 +44,19 @@ public class Category implements Serializable {
@OneToMany(mappedBy = "category")
protected List listPosts;
+ /* ******************* */
+ /* Constructors */
+ /* ******************* */
+ public Category() {
+ super();
+ }
+
+ public Category(final CategoryDTO pCategory) {
+ this();
+ id = pCategory.getId();
+ name = pCategory.getName();
+ }
+
/* ******************* */
/* Getters & Setters */
/* ******************* */
diff --git a/src/main/java/org/codiki/core/entities/persistence/Post.java b/src/main/java/org/codiki/core/entities/persistence/Post.java
index 289cc9d..0182915 100644
--- a/src/main/java/org/codiki/core/entities/persistence/Post.java
+++ b/src/main/java/org/codiki/core/entities/persistence/Post.java
@@ -18,7 +18,10 @@ import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
+import org.codiki.core.entities.dto.PostDTO;
import org.codiki.core.utils.DateUtils;
+import org.hibernate.annotations.Generated;
+import org.hibernate.annotations.GenerationTime;
@Entity
@Table(name="post")
@@ -35,6 +38,8 @@ public class Post implements Serializable {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+ // This annotation serves to fetch the attribute after an insert into db
+ @Generated(GenerationTime.ALWAYS)
private String key;
private String title;
@@ -70,6 +75,15 @@ public class Post implements Serializable {
public Post() {
super();
}
+
+ public Post(final PostDTO pPost) {
+ title = pPost.getTitle();
+ image = pPost.getImage();
+ description = pPost.getDescription();
+ creationDate = new Date();
+ text = pPost.getText();
+ category = new Category(pPost.getCategory());
+ }
/* ******************* */
/* Getters & Setters */
diff --git a/src/main/java/org/codiki/core/entities/security/Token.java b/src/main/java/org/codiki/core/entities/security/Token.java
index 15afb7e..dc1b95f 100644
--- a/src/main/java/org/codiki/core/entities/security/Token.java
+++ b/src/main/java/org/codiki/core/entities/security/Token.java
@@ -11,7 +11,7 @@ public class Token {
private static final int DELAY = 30;
/** The Constant BITS_NUMBER. */
- private static final int BITS_NUMBER = 130;
+ private static final int BITS_NUMBER = 1000;
/** The Constant RADIX. */
private static final int RADIX = 32;
diff --git a/src/main/java/org/codiki/core/repositories/CategoryRepository.java b/src/main/java/org/codiki/core/repositories/CategoryRepository.java
new file mode 100644
index 0000000..104c8b2
--- /dev/null
+++ b/src/main/java/org/codiki/core/repositories/CategoryRepository.java
@@ -0,0 +1,10 @@
+package org.codiki.core.repositories;
+
+import org.codiki.core.entities.persistence.Category;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CategoryRepository extends CrudRepository {
+
+}
diff --git a/src/main/java/org/codiki/core/repositories/PostRepository.java b/src/main/java/org/codiki/core/repositories/PostRepository.java
index 460ffc8..f428d36 100644
--- a/src/main/java/org/codiki/core/repositories/PostRepository.java
+++ b/src/main/java/org/codiki/core/repositories/PostRepository.java
@@ -1,10 +1,32 @@
package org.codiki.core.repositories;
+import java.util.List;
+import java.util.Optional;
+
import org.codiki.core.entities.persistence.Post;
+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 PostRepository extends CrudRepository {
+ @Query("SELECT p FROM Post p WHERE p.key = :postKey")
+ Optional getByKey(@Param("postKey") final String pPostKey);
+
+ @Query(value = "SELECT * FROM post p INNER JOIN \"user\" u ON u.id = p.creator_id ORDER BY p.creation_date DESC LIMIT :limit",
+ nativeQuery = true)
+ List getLast(@Param("limit") final Integer pLimit);
+
+ @Query(value = "SELECT * FROM post p WHERE category_id = :categoryId ORDER BY creation_date DESC",
+ nativeQuery = true)
+ List getByCategoryId(@Param("categoryId") final Long pCategoryId);
+
+ @Query(value = "SELECT * FROM post WHERE creator_id = :creatorId ORDER BY creation_date DESC",
+ nativeQuery = true)
+ List getByCreator(@Param("creatorId") final Long pCreatorId);
+
+ @Query(value = "SELECT * FROM Post WHERE id = :id", nativeQuery = true)
+ Optional getOne(@Param("id") final Long id);
}
diff --git a/src/main/java/org/codiki/core/repositories/UserRepository.java b/src/main/java/org/codiki/core/repositories/UserRepository.java
index eec187a..2f224fa 100644
--- a/src/main/java/org/codiki/core/repositories/UserRepository.java
+++ b/src/main/java/org/codiki/core/repositories/UserRepository.java
@@ -2,12 +2,38 @@ package org.codiki.core.repositories;
import java.util.Optional;
+import javax.transaction.Transactional;
+
import org.codiki.core.entities.persistence.User;
+import org.springframework.data.jpa.repository.Modifying;
+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 UserRepository extends CrudRepository {
+
Optional findByEmail(@Param("email") final String pEmail);
+
+ /**
+ * Checks if the password in parameters is the passwords of the user in
+ * database.
+ *
+ * @param pId
+ * The user id.
+ * @param pPassword
+ * The password to check.
+ * @return {@code true} if the password is the user password in database,
+ * {@code false} otherwise.
+ */
+ @Query(value = "SELECT CASE WHEN EXISTS(" +
+ " SELECT id FROM \"user\" WHERE id = :id AND password = :password" +
+ ") THEN TRUE ELSE FALSE END", nativeQuery = true)
+ boolean checkPassword(@Param("id") final Long pId, @Param("password") final String pPassword);
+
+ @Query(value = "UPDATE \"user\" SET password = :password WHERE id = :id", nativeQuery = true)
+ @Transactional
+ @Modifying
+ void updatePassword(@Param("id") final Long pId, @Param("password") final String pPassword);
}
diff --git a/src/main/java/org/codiki/core/security/AuthenticationFilter.java b/src/main/java/org/codiki/core/security/AuthenticationFilter.java
index 50b4819..d2fb4ed 100644
--- a/src/main/java/org/codiki/core/security/AuthenticationFilter.java
+++ b/src/main/java/org/codiki/core/security/AuthenticationFilter.java
@@ -6,7 +6,6 @@ import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -26,7 +25,7 @@ public class AuthenticationFilter extends AbstractFilter {
@Override
protected List getRoutes() {
return Arrays.asList(
- new Route("\\/api\\/posts.*")
+ new Route("\\/api\\/posts\\/myPosts")
);
}
@@ -34,7 +33,7 @@ public class AuthenticationFilter extends AbstractFilter {
protected void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Token : " + request.getHeader("token"));
- if(tokenService.isUserConnected(request.getHeader("token"))) {
+ if("OPTIONS".equals(request.getMethod()) || tokenService.isUserConnected(request.getHeader("token"))) {
chain.doFilter(request, response);
} else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
diff --git a/src/main/java/org/codiki/core/security/TokenService.java b/src/main/java/org/codiki/core/security/TokenService.java
index d0ccbe4..2b24b85 100644
--- a/src/main/java/org/codiki/core/security/TokenService.java
+++ b/src/main/java/org/codiki/core/security/TokenService.java
@@ -1,8 +1,12 @@
package org.codiki.core.security;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import javax.servlet.http.HttpServletRequest;
+
import org.codiki.core.entities.persistence.User;
import org.springframework.stereotype.Service;
@@ -10,6 +14,8 @@ import org.springframework.stereotype.Service;
public class TokenService {
/** Map of connected users. */
private static final Map connectedUsers;
+
+ private static final String HEADER_TOKEN = "token";
/**
* Class constructor
@@ -49,11 +55,15 @@ public class TokenService {
*/
@SuppressWarnings("unlikely-arg-type")
private void clearExpiredUsers() {
- connectedUsers.entrySet().stream().forEach(user -> {
- if(!user.getValue().getToken().isValid()) {
- connectedUsers.remove(user).getKey();
- }
- });
+ synchronized (this) {
+ List usersToRemove = new LinkedList<>();
+ connectedUsers.entrySet().stream().forEach(user -> {
+ if(!user.getValue().getToken().isValid()) {
+ usersToRemove.add(user.getValue());
+ }
+ });
+ usersToRemove.stream().forEach(connectedUsers::remove);
+ }
}
/**
@@ -103,4 +113,8 @@ public class TokenService {
connectedUsers.remove(pToken);
}
}
+
+ public User getAuthenticatedUserByToken(final HttpServletRequest pRequest) {
+ return connectedUsers.get(pRequest.getHeader(HEADER_TOKEN));
+ }
}
diff --git a/src/main/java/org/codiki/core/services/ParserService.java b/src/main/java/org/codiki/core/services/ParserService.java
new file mode 100644
index 0000000..1533971
--- /dev/null
+++ b/src/main/java/org/codiki/core/services/ParserService.java
@@ -0,0 +1,131 @@
+package org.codiki.core.services;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.codiki.core.utils.StringUtils;
+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 = "\\2
";
+ private static final String REG_IMAGES = "\\[img src="([^\"| ]+)"( alt="([^\"| ]+)")? \\/\\]";
+ private static final String REG_IMAGES_REPLACE = "
";
+ private static final String REG_LINKS = "\\[link href="([^\"| ]+)" txt="([^\"| ]+)" \\/\\]";
+ private static final String REG_LINKS_REPLACE = "\\2";
+
+ 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;
+
+ static {
+ PATTERN_CODE = Pattern.compile(REG_CODE);
+ PATTERN_CODE_REPLACE = Pattern.compile(REG_CODE_REPLACE);
+ PATTERN_IMAGES = Pattern.compile(REG_IMAGES);
+ PATTERN_IMAGES_REPLACE = Pattern.compile(REG_IMAGES_REPLACE);
+ PATTERN_LINKS = Pattern.compile(REG_LINKS);
+ PATTERN_LINKS_REPLACE = Pattern.compile(REG_LINKS_REPLACE);
+ }
+
+ public String parse(String pSource) {
+ return unParseDolars(parseCode(parseHeaders(parseImages(parseLinks(parseBackSpaces(StringEscapeUtils.escapeHtml(parseDolars(pSource))))))));
+ }
+
+ private String parseDolars(final String pSource) {
+ return pSource.replace("$", "£ø");
+ }
+
+ private String unParseDolars(final String pSource) {
+ return pSource.replace("£ø", "$");
+ }
+
+ protected String parseHeaders(final String pSource) {
+ String result = pSource;
+ for(int i = 1 ; i <= 3 ; i++) {
+ result = parseHeadersHX(result, i);
+ }
+ return result;
+ }
+
+ protected String parseHeadersHX(final String pSource, final int pNumHeader) {
+ String result = pSource;
+
+ // (.*)(\[hX\](.*)\[\/hX\])+(.*)
+ final String regex = StringUtils.concat("(.*)(\\[h", pNumHeader, "\\](.*)\\[\\/h", pNumHeader, "\\])+(.*)");
+
+ final Pattern pattern = Pattern.compile(regex);
+
+ Matcher matcher = pattern.matcher(result);
+
+ while(matcher.find()) {
+ // \1\3\4
+ result = matcher.replaceFirst(StringUtils.concat(matcher.group(1),
+ "", matcher.group(3), "", matcher.group(4)));
+ matcher = pattern.matcher(result);
+ }
+
+ return result;
+ }
+
+ protected String parseBackSpaces(final String pSource) {
+ return pSource.replaceAll("\r?\n", "
").replaceAll("\\[\\/code\\]
", "[/code]\n");
+ }
+
+ protected String parseImages(final String pSource) {
+ String result = pSource;
+
+ Matcher matcher = PATTERN_IMAGES.matcher(result);
+
+ while(matcher.find()) {
+ String altStr = matcher.group(3);
+
+ if(altStr == null) {
+ altStr = "";
+ }
+
+ result = matcher.replaceFirst(StringUtils.concat("
"));
+ matcher = PATTERN_IMAGES.matcher(result);
+ }
+
+ return result;
+ }
+
+ protected String parseLinks(final String pSource) {
+ String result = pSource;
+
+ Matcher matcher = PATTERN_LINKS.matcher(result);
+
+ while(matcher.find()) {
+ result = matcher.replaceFirst(StringUtils.concat("", matcher.group(2), ""));
+ matcher = PATTERN_LINKS.matcher(result);
+ }
+
+ return result;
+ }
+
+ protected String parseCode(final String pSource) {
+ String result = pSource;
+
+ Matcher matcher = PATTERN_CODE.matcher(result);
+
+ while(matcher.find()) {
+ // replace the '
' in group by '\n'
+ String codeContent = matcher.group(2).replaceAll("
", "\n");
+ if(codeContent.startsWith("\n")) {
+ codeContent = codeContent.substring(1);
+ }
+
+ result = matcher.replaceFirst(StringUtils.concat("", codeContent, "
"));
+ matcher = PATTERN_CODE.matcher(result);
+ }
+
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/codiki/login/LoginService.java b/src/main/java/org/codiki/login/LoginService.java
deleted file mode 100644
index 1e92141..0000000
--- a/src/main/java/org/codiki/login/LoginService.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.codiki.login;
-
-import java.util.Optional;
-
-import javax.naming.AuthenticationException;
-import javax.servlet.http.HttpServletResponse;
-
-import org.codiki.core.entities.dto.UserDAO;
-import org.codiki.core.entities.persistence.User;
-import org.codiki.core.repositories.UserRepository;
-import org.codiki.core.security.TokenService;
-import org.codiki.core.utils.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-@Service
-public class LoginService {
-
- @Autowired
- private UserRepository userRepository;
-
- @Autowired
- private TokenService tokenService;
-
- /**
- * Check the user credentials and generate him a token if they are correct.
- *
- * @param pUser
- * The user sent from client.
- * @return The user populated with the generated token.
- * @throws AuthenticationException
- * If the credentials are wrong.
- */
- public UserDAO checkCredentials(HttpServletResponse pResponse, UserDAO pUser) {
- UserDAO result = null;
-
- Optional user = userRepository.findByEmail(pUser.getEmail());
-
- if(user.isPresent() && StringUtils.compareHash(pUser.getPassword(), user.get().getPassword())) {
- tokenService.addUser(user.get());
- result = new UserDAO(user.get());
- } else {
- pResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- }
-
- return result;
- }
-
-
-}
diff --git a/src/main/java/org/codiki/posts/PostController.java b/src/main/java/org/codiki/posts/PostController.java
index da819d7..91f8634 100644
--- a/src/main/java/org/codiki/posts/PostController.java
+++ b/src/main/java/org/codiki/posts/PostController.java
@@ -1,9 +1,23 @@
package org.codiki.posts;
+import java.util.List;
+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;
import org.codiki.core.entities.persistence.Post;
import org.codiki.core.repositories.PostRepository;
+import org.codiki.core.security.TokenService;
+import org.codiki.core.services.ParserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -11,11 +25,92 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/posts")
public class PostController {
+ private static final int LIMIT_POSTS_HOME = 20;
+
+ @Autowired
+ private ParserService parserService;
+
@Autowired
private PostRepository postRepository;
+ @Autowired
+ private TokenService tokenService;
+
+ @Autowired
+ private PostService postService;
+
@GetMapping
- public Iterable getAll() {
- return postRepository.findAll();
+ public List getAll() {
+ return StreamSupport.stream(postRepository.findAll().spliterator(), false)
+ .map(PostDTO::new).collect(Collectors.toList());
+ }
+
+ @GetMapping("/{postKey}")
+ public PostDTO getByKey(@PathVariable("postKey") final String pPostKey,
+ final HttpServletResponse response) {
+ PostDTO result = null;
+
+ final Optional post = postRepository.getByKey(pPostKey);
+ if(post.isPresent()) {
+ result = new PostDTO(post.get());
+ result.setText(parserService.parse(result.getText()));
+ } else {
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+
+ return result;
+ }
+
+ @GetMapping("/last")
+ public List getLast() {
+ return postRepository.getLast(LIMIT_POSTS_HOME).stream()
+ .map(PostDTO::new).collect(Collectors.toList());
+ }
+
+ @PostMapping("/search")
+ public List search() {
+ // TODO
+ return null;
+ }
+
+ @GetMapping("/byCategory/{categoryId}")
+ public List getByCategory(@PathVariable("categoryId") final Long pCategoryId) {
+ return postRepository.getByCategoryId(pCategoryId).stream()
+ .map(PostDTO::new).collect(Collectors.toList());
+ }
+
+ @GetMapping("/myPosts")
+ public List getMyPosts(final HttpServletRequest pRequest, final HttpServletResponse pResponse) {
+ return postRepository.getByCreator(tokenService
+ .getAuthenticatedUserByToken(pRequest).getId())
+ .parallelStream().map(PostDTO::new).collect(Collectors.toList());
+ }
+
+ @PostMapping("/preview")
+ public PostDTO preview(@RequestBody final PostDTO pPost) {
+ final PostDTO result = new PostDTO();
+
+ result.setTitle(pPost.getTitle());
+ result.setImage(pPost.getImage() == null || "".equals(pPost.getImage())
+ ? "https://news-cdn.softpedia.com/images/news2/this-is-the-default-wallpaper-of-the-gnome-3-20-desktop-environment-500743-2.jpg"
+ : pPost.getImage());
+ result.setDescription(pPost.getDescription());
+ result.setText(parserService.parse(pPost.getText()));
+
+ return result;
+ }
+
+ @PostMapping("/")
+ public PostDTO insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
+ final HttpServletResponse pResponse) {
+ PostDTO result = null;
+
+ Optional postCreated = postService.insert(pPost, pRequest, pResponse);
+
+ if(postCreated.isPresent()) {
+ result = new PostDTO(postCreated.get());
+ }
+
+ return result;
}
}
diff --git a/src/main/java/org/codiki/posts/PostService.java b/src/main/java/org/codiki/posts/PostService.java
new file mode 100644
index 0000000..5e741db
--- /dev/null
+++ b/src/main/java/org/codiki/posts/PostService.java
@@ -0,0 +1,41 @@
+package org.codiki.posts;
+
+import java.util.Optional;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.codiki.core.entities.dto.PostDTO;
+import org.codiki.core.entities.persistence.Post;
+import org.codiki.core.repositories.PostRepository;
+import org.codiki.core.security.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PostService {
+
+ @Autowired
+ private PostRepository postRepository;
+
+ @Autowired
+ private TokenService tokenService;
+
+ public Optional insert(final PostDTO pPost, final HttpServletRequest pRequest,
+ final HttpServletResponse pResponse) {
+ Optional result = Optional.empty();
+
+ final String userToken = pRequest.getHeader("token");
+
+ if(userToken.equals(pPost.getAuthor().getToken())) {
+ final Post postToSave = new Post(pPost);
+ postToSave.setAuthor(tokenService.getAuthenticatedUserByToken(pRequest));
+ postRepository.save(postToSave);
+ result = Optional.of(postToSave);
+ } else {
+ pResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ }
+
+ return result;
+ }
+}