diff --git a/demo-thym.iml b/demo-thym.iml new file mode 100644 index 0000000..c123ed6 --- /dev/null +++ b/demo-thym.iml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/home/quangminh/uploader/13224234_590858354408064_771242502_o.png b/home/quangminh/uploader/13224234_590858354408064_771242502_o.png new file mode 100644 index 0000000..4c00a0e Binary files /dev/null and b/home/quangminh/uploader/13224234_590858354408064_771242502_o.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a555377 --- /dev/null +++ b/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + demo-thym + demo-thym + 1.0-SNAPSHOT + jar + + SpringThymeleaf + Demo project for Spring Boot + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + + + + com.google.guava + guava + 18.0 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + mysql + mysql-connector-java + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.webjars + bootstrap + 3.3.7 + + + + + + org.elasticsearch + elasticsearch + 1.6.0 + + + + + org.webjars + jquery + 2.1.4 + + + + org.springframework.boot + spring-boot-starter-security + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + + + + org.webjars + bootstrap + 3.3.7 + + + + + org.webjars + jquery + 2.1.4 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + + + \ No newline at end of file diff --git a/src/main/java/mvs/SpringApplication.java b/src/main/java/mvs/SpringApplication.java new file mode 100644 index 0000000..32ce526 --- /dev/null +++ b/src/main/java/mvs/SpringApplication.java @@ -0,0 +1,44 @@ +package mvs; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Created by quangminh on 27/06/2017. + */ +@Configuration +@ComponentScan(basePackageClasses = SpringApplication.class) +@EnableJpaRepositories(basePackages = "mvs.repository") +@EnableTransactionManagement +@EnableAutoConfiguration +@SpringBootApplication +public class SpringApplication extends SpringBootServletInitializer { + + @Bean + public JpaVendorAdapter jpaVendorAdapter() { + HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); + jpaVendorAdapter.setDatabase(Database.MYSQL); + jpaVendorAdapter.setShowSql(true); + jpaVendorAdapter.setGenerateDdl(true); + return jpaVendorAdapter; + } + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(SpringApplication.class); + } + + public static void main(String[] args) { + org.springframework.boot.SpringApplication.run(SpringApplication.class, args); + } + +} diff --git a/src/main/java/mvs/config/AppConfig.java b/src/main/java/mvs/config/AppConfig.java new file mode 100644 index 0000000..e1299f3 --- /dev/null +++ b/src/main/java/mvs/config/AppConfig.java @@ -0,0 +1,27 @@ +package mvs.config; + +import org.elasticsearch.client.Client; + +/** + * Created by quangminh on 07/07/2017. + */ +public class AppConfig { + static String imageSrc; + static Client client; + + public static Client getClient() { + return client; + } + + public static void setClient(Client client) { + AppConfig.client = client; + } + + public static String getImageSrc() { + return imageSrc; + } + + public static void setImageSrc(String imageSrc) { + AppConfig.imageSrc = imageSrc; + } +} diff --git a/src/main/java/mvs/config/CustomUserDetails.java b/src/main/java/mvs/config/CustomUserDetails.java new file mode 100644 index 0000000..eef526c --- /dev/null +++ b/src/main/java/mvs/config/CustomUserDetails.java @@ -0,0 +1,18 @@ +package mvs.config; + + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.util.Collection; + +/** + * Created by quangminh on 25/07/2017. + */ +public class CustomUserDetails extends User { + public CustomUserDetails(String username, String password, Collection authorities) { + super(username, password, authorities); + } + private String firstName; + private String lastName; +} diff --git a/src/main/java/mvs/config/DataSeedingListener.java b/src/main/java/mvs/config/DataSeedingListener.java new file mode 100644 index 0000000..7fc646c --- /dev/null +++ b/src/main/java/mvs/config/DataSeedingListener.java @@ -0,0 +1,63 @@ +package mvs.config; + + +import mvs.model.Role; +import mvs.model.User; +import mvs.repository.RoleRepository; +import mvs.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import java.util.HashSet; + +@Component +public class DataSeedingListener implements ApplicationListener { + + @Autowired + private UserRepository userRepository; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public void onApplicationEvent(ContextRefreshedEvent arg0) { + // Roles + if (roleRepository.findByName("ROLE_ADMIN") == null) { + roleRepository.save(new Role("ROLE_ADMIN")); + } + + if (roleRepository.findByName("ROLE_MEMBER") == null) { + roleRepository.save(new Role("ROLE_MEMBER")); + } + + // Admin account + if (userRepository.findByUsername("admin@gmail.com") == null) { + User admin = new User(); + admin.setUsername("admin@gmail.com"); + admin.setPassword(passwordEncoder.encode("123456")); + HashSet roles = new HashSet(); + roles.add(roleRepository.findByName("ROLE_ADMIN")); + roles.add(roleRepository.findByName("ROLE_MEMBER")); + admin.setRoles(roles); + userRepository.save(admin); + } + + // Member account + if (userRepository.findByUsername("member@gmail.com") == null) { + User user = new User(); + user.setUsername("member@gmail.com"); + user.setPassword(passwordEncoder.encode("123456")); + HashSet roles = new HashSet(); + roles.add(roleRepository.findByName("ROLE_MEMBER")); + user.setRoles(roles); + userRepository.save(user); + } + } + +} diff --git a/src/main/java/mvs/config/LoadConfig.java b/src/main/java/mvs/config/LoadConfig.java new file mode 100644 index 0000000..7e65a90 --- /dev/null +++ b/src/main/java/mvs/config/LoadConfig.java @@ -0,0 +1,37 @@ +package mvs.config; + +import com.google.common.io.Resources; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.transport.InetSocketTransportAddress; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Properties; + +/** + * Created by quangminh on 07/07/2017. + */ +@Component +public class LoadConfig { + @EventListener + public void init(ContextRefreshedEvent event) { + AppConfig.client = new TransportClient() + .addTransportAddress(new InetSocketTransportAddress("127.0.0.1", 9300)); + URL url = Resources.getResource("application.properties"); + Properties prop = new Properties(); + try { + prop.load(url.openStream()); + AppConfig.setImageSrc(prop.getProperty("image.source")); + + } catch (IOException e) { + e.printStackTrace(); + } + + + } +} diff --git a/src/main/java/mvs/config/WebMvcConfig.java b/src/main/java/mvs/config/WebMvcConfig.java new file mode 100644 index 0000000..5cb4449 --- /dev/null +++ b/src/main/java/mvs/config/WebMvcConfig.java @@ -0,0 +1,26 @@ +package mvs.config; + +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration + +public class WebMvcConfig extends WebMvcConfigurerAdapter { + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource bean = new ReloadableResourceBundleMessageSource(); + bean.addBasenames("classpath:messages"); + return bean; + } +/* @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/images*//**").addResourceLocations("file:"+AppConfig.getImageSrc()); + }*/ +} diff --git a/src/main/java/mvs/config/WebSecurityConfig.java b/src/main/java/mvs/config/WebSecurityConfig.java new file mode 100644 index 0000000..58c7227 --- /dev/null +++ b/src/main/java/mvs/config/WebSecurityConfig.java @@ -0,0 +1,57 @@ +package mvs.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private UserDetailsService userDetailsService; + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/register").permitAll() + .antMatchers("/").hasRole("MEMBER") + .antMatchers("/contact/create").hasRole("MEMBER") + .antMatchers("/form").hasRole("MEMBER") + .antMatchers("/upload").hasRole("MEMBER") + .antMatchers("/users/edit").hasRole("MEMBER") + .antMatchers("/posts/upload").hasRole("MEMBER") + + .antMatchers("/admin").hasRole("ADMIN") + .and() + .formLogin() + .loginPage("/login") + .usernameParameter("username") + .passwordParameter("password") + .defaultSuccessUrl("/") + .failureUrl("/login?error") + .and() + .exceptionHandling() + .accessDeniedPage("/403"); + } + +} \ No newline at end of file diff --git a/src/main/java/mvs/controller/CurrentUserController.java b/src/main/java/mvs/controller/CurrentUserController.java new file mode 100644 index 0000000..939c698 --- /dev/null +++ b/src/main/java/mvs/controller/CurrentUserController.java @@ -0,0 +1,22 @@ +package mvs.controller; + +import mvs.model.User; +import mvs.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; + +/** + * Created by quangminh on 26/07/2017. + */ +@ControllerAdvice +public class CurrentUserController { + + @Autowired + UserService userService; + @ModelAttribute("currentUser") + public User getCurrentUser(@AuthenticationPrincipal User user){ + return userService.getCurentUser(); + } +} diff --git a/src/main/java/mvs/controller/FileUploadController.java b/src/main/java/mvs/controller/FileUploadController.java new file mode 100644 index 0000000..310998d --- /dev/null +++ b/src/main/java/mvs/controller/FileUploadController.java @@ -0,0 +1,134 @@ +package mvs.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * Handles requests for the application file upload requests + */ +@Controller +public class FileUploadController { + public static final String ROOT = "/home/quangminh"; + + private static final Logger logger = LoggerFactory + .getLogger(FileUploadController.class); + + /** + * Upload single file using Spring Controller + */ + @RequestMapping(value = "/uploadFile", method = RequestMethod.POST) + public + @ResponseBody + String uploadFileHandler( + @RequestParam(value = "file", required = true) MultipartFile file + ) { + String name = file.getOriginalFilename(); + + if (!file.isEmpty()) { + try { + file.getSize(); + byte[] bytes = file.getBytes(); + + // Creating the directory to store file + File dir = new File(ROOT + File.separator + "tmpFiles"); + if (!dir.exists()) + dir.mkdirs(); + + // Create the file on server + File serverFile = new File(dir.getAbsolutePath() + + File.separator + name); + BufferedOutputStream stream = new BufferedOutputStream( + new FileOutputStream(serverFile)); + stream.write(bytes); + stream.close(); + + logger.info("Server File Location=" + + serverFile.getAbsolutePath()); + + return "redirect:/"; + } catch (Exception e) { + return "You failed to upload " + name + " => " + e.getMessage(); + } + } else { + return "You failed to upload " + name + + " because the file was empty."; + } + } + + /** + * Upload multiple file using Spring Controller + */ + @RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST) + public + @ResponseBody + String uploadMultipleFileHandler(@RequestParam("name") String[] names, + @RequestParam("file") MultipartFile[] files) { + + if (files.length != names.length) + return "Mandatory information missing"; + + String message = ""; + for (int i = 0; i < files.length; i++) { + MultipartFile file = files[i]; + String name = names[i]; + try { + byte[] bytes = file.getBytes(); + + // Creating the directory to store file + String rootPath = System.getProperty("catalina.home"); + File dir = new File(rootPath + File.separator + "tmpFiles"); + if (!dir.exists()) + dir.mkdirs(); + + // Create the file on server + File serverFile = new File(dir.getAbsolutePath() + + File.separator + name); + BufferedOutputStream stream = new BufferedOutputStream( + new FileOutputStream(serverFile)); + stream.write(bytes); + stream.close(); + + logger.info("Server File Location=" + + serverFile.getAbsolutePath()); + + message = message + "You successfully uploaded file=" + name + + "
"; + } catch (Exception e) { + return "You failed to upload " + name + " => " + e.getMessage(); + } + } + return message; + } + + + @RequestMapping(method = RequestMethod.POST, value = "/uploadFile2") + public String handleFileUpload(@RequestParam("file") MultipartFile file, + RedirectAttributes redirectAttributes) { + + if (!file.isEmpty()) { + try { + Files.copy(file.getInputStream(), Paths.get(ROOT, file.getOriginalFilename())); + redirectAttributes.addFlashAttribute("message", + "You successfully uploaded " + file.getOriginalFilename() + "!"); + } catch (IOException | RuntimeException e) { + redirectAttributes.addFlashAttribute("message", "Failued to upload " + file.getOriginalFilename() + " => " + e.getMessage()); + } + } else { + redirectAttributes.addFlashAttribute("message", "Failed to upload " + file.getOriginalFilename() + " because it was empty"); + } + + final String redirectUrl = "redirect:http://localhost/test.php"; + return redirectUrl; + } +} diff --git a/src/main/java/mvs/controller/PostController.java b/src/main/java/mvs/controller/PostController.java new file mode 100644 index 0000000..a2edbee --- /dev/null +++ b/src/main/java/mvs/controller/PostController.java @@ -0,0 +1,62 @@ +package mvs.controller; + +import mvs.config.AppConfig; +import mvs.model.Post; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Created by quangminh on 25/07/2017. + */ +@Controller +public class PostController { + + + @GetMapping("/") + public String postLine(Model model){ + return "post/list_post"; + } + @GetMapping("/posts/upload") + public String uploadPost(Model model){ + model.addAttribute("itPost", new Post()); + + return "post/tinymce"; + } + + @PostMapping("/posts/save") + public String save( @RequestParam("file") MultipartFile file, + @ModelAttribute("itPost") Post itPost, + BindingResult result, + RedirectAttributes redirect) throws IOException { + if (!file.isEmpty()) { + File dir = new File(AppConfig.getImageSrc()); + if (!dir.exists()) + dir.mkdirs(); + byte[] bytes = file.getBytes(); + Path path = Paths.get(AppConfig.getImageSrc() + file.getOriginalFilename()); + Files.write(path, bytes); + itPost.setImage(file.getOriginalFilename()); + redirect.addFlashAttribute("message", + "You successfully uploaded '" + file.getOriginalFilename() + "'"); + } + itPost.setCreateAt(System.currentTimeMillis()); + + + return "redirect:/"; + } + + +} diff --git a/src/main/java/mvs/controller/UserController.java b/src/main/java/mvs/controller/UserController.java new file mode 100644 index 0000000..74bacfd --- /dev/null +++ b/src/main/java/mvs/controller/UserController.java @@ -0,0 +1,127 @@ +package mvs.controller; + +import mvs.config.AppConfig; +import mvs.model.User; +import mvs.service.SecurityService; +import mvs.service.UserService; +import mvs.validator.UserValidator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Created by quangminh on 27/06/2017. + */ +@Controller +public class UserController { + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + UserService userService; + @Autowired + SecurityService securityService; + @Autowired + UserValidator userValidator; + + + @GetMapping("/users/edit") + public String editUser(Model model) { + model.addAttribute("user", userService.getCurentUser()); + + return "profile/profile"; + } + + @PostMapping("/users/edit") + public String save( + @RequestParam("file") MultipartFile file, + @ModelAttribute("user") User user, + BindingResult result, + RedirectAttributes redirect, + Model model) + throws IOException { + + user.setRoles(userService.getCurentUser().getRoles()); + if (!file.isEmpty()) { + File dir = new File(AppConfig.getImageSrc()); + if (!dir.exists()) + dir.mkdirs(); + byte[] bytes = file.getBytes(); + Path path = Paths.get(AppConfig.getImageSrc() + file.getOriginalFilename()); + Files.write(path, bytes); + user.setAvatar(file.getOriginalFilename()); + redirect.addFlashAttribute("message", + "You successfully uploaded '" + file.getOriginalFilename() + "'"); + } + userService.save(user); + return "redirect:/"; + } + + @GetMapping("/admin") + public String admin() { + return "admin/admin"; + } + + @GetMapping("/403") + public String accessDenied() { + return "error/403"; + } + + @GetMapping("/login") + public String getLogin() { + return "security/login"; + } + + @GetMapping("/signup") + public String getRegister(Model model) { + model.addAttribute("user", new User()); + + return "security/register"; + } + + @GetMapping("/logout") + public String logout(HttpServletRequest request, HttpServletResponse response) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null) { + new SecurityContextLogoutHandler().logout(request, response, auth); + } + return "redirect:/security/login"; + } + + @PostMapping("/signup/save") + public String postRegister( + @Valid User user, + BindingResult result, + Model model + ) { + userValidator.validate(user, result); + if (result.hasErrors()) { + return "security/register"; + } + + userService.saveAndGrantRole(user); + + return "redirect:/login"; + + + } + + + +} diff --git a/src/main/java/mvs/model/Category.java b/src/main/java/mvs/model/Category.java new file mode 100644 index 0000000..92c4791 --- /dev/null +++ b/src/main/java/mvs/model/Category.java @@ -0,0 +1,49 @@ +package mvs.model; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by quangminh on 31/07/2017. + */ +@Entity +public class Category { + + @Id + @GeneratedValue + Long id; + + String name; + + + @ManyToMany(mappedBy = "categories") + List posts; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Category() { + } + + public List getPosts() { + return posts; + } + + public void setPosts(List posts) { + this.posts = posts; + } +} diff --git a/src/main/java/mvs/model/Post.java b/src/main/java/mvs/model/Post.java new file mode 100644 index 0000000..02cc4f4 --- /dev/null +++ b/src/main/java/mvs/model/Post.java @@ -0,0 +1,95 @@ +package mvs.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by quangminh on 28/07/2017. + */ +@Entity +public class Post +{ + @Id + @GeneratedValue + Long id; + + String title; + String body; + String image; + Long createAt; + + + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable(name = "post_category", joinColumns = @JoinColumn(name = "post_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "category_id", referencedColumnName = "id")) + List categories=new ArrayList<>(); + + + @JsonIgnore + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = User.class) + User user; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public List getCategories() { + return categories; + } + + public void setCategories(List categories) { + this.categories = categories; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getCreateAt() { + return createAt; + } + + public void setCreateAt(Long createAt) { + this.createAt = createAt; + } + + public Post() { + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + +} diff --git a/src/main/java/mvs/model/Role.java b/src/main/java/mvs/model/Role.java new file mode 100644 index 0000000..b8ad2fc --- /dev/null +++ b/src/main/java/mvs/model/Role.java @@ -0,0 +1,53 @@ +package mvs.model; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Set; + +/** + * Created by quangminh on 27/06/2017. + */ +@Entity +public class Role { + @Id + @GeneratedValue + Long id; + + @Column(name = "name", nullable = false) + String name; + + + @ManyToMany(mappedBy = "roles") + private Set users; + + public Role(String name) { + this.name = name; + } + + public Role() { + } + + 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; + } + + public Set getUsers() { + return users; + } + + public void setUsers(Set users) { + this.users = users; + } +} diff --git a/src/main/java/mvs/model/User.java b/src/main/java/mvs/model/User.java new file mode 100644 index 0000000..b75dfe5 --- /dev/null +++ b/src/main/java/mvs/model/User.java @@ -0,0 +1,145 @@ +package mvs.model; + +import org.hibernate.validator.constraints.Email; +import org.omg.CORBA.DynSequence; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Created by quangminh on 27/06/2017. + */ +@Entity +public class User { + @Id + @GeneratedValue + Long id; + + + @Email + @Column(name = "username", nullable = false, unique = true) + private String username; + + @NotNull + @Column(name = "password", nullable = false) + private String password; + private String passwordConfirm; + + String nickname; + String avatar; + String company; + String firstname; + String lastname; + + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } + + @ManyToMany + @JoinTable( + name = "user_role", + joinColumns = @JoinColumn(name = "user_id"), + inverseJoinColumns = @JoinColumn(name = "role_id") + ) + private Set roles; + + + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL) + List posts=new ArrayList<>(); + + public List getPosts() { + return posts; + } + + public void setPosts(List posts) { + this.posts = posts; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public User() { + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } +} diff --git a/src/main/java/mvs/repository/RoleRepository.java b/src/main/java/mvs/repository/RoleRepository.java new file mode 100644 index 0000000..43ec901 --- /dev/null +++ b/src/main/java/mvs/repository/RoleRepository.java @@ -0,0 +1,12 @@ +package mvs.repository; + +import mvs.model.Role; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Created by quangminh on 27/06/2017. + */ +public interface RoleRepository extends JpaRepository{ + Role findByName(String name); + +} diff --git a/src/main/java/mvs/repository/UserRepository.java b/src/main/java/mvs/repository/UserRepository.java new file mode 100644 index 0000000..90b07d9 --- /dev/null +++ b/src/main/java/mvs/repository/UserRepository.java @@ -0,0 +1,12 @@ +package mvs.repository; + +import mvs.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Created by quangminh on 27/06/2017. + */ +public interface UserRepository extends JpaRepository { + User findByUsername(String email); + +} diff --git a/src/main/java/mvs/service/SecurityService.java b/src/main/java/mvs/service/SecurityService.java new file mode 100644 index 0000000..5c997b5 --- /dev/null +++ b/src/main/java/mvs/service/SecurityService.java @@ -0,0 +1,7 @@ +package mvs.service; + +public interface SecurityService { + String findLoggedInUsername(); + + void autologin(String username, String password); +} diff --git a/src/main/java/mvs/service/UserService.java b/src/main/java/mvs/service/UserService.java new file mode 100644 index 0000000..b4d80d1 --- /dev/null +++ b/src/main/java/mvs/service/UserService.java @@ -0,0 +1,11 @@ +package mvs.service; + + +import mvs.model.User; + +public interface UserService { + void save(User user); + void saveAndGrantRole(User user); + User findByUsername(String username); + User getCurentUser(); +} diff --git a/src/main/java/mvs/service/impl/SecurityServiceImpl.java b/src/main/java/mvs/service/impl/SecurityServiceImpl.java new file mode 100644 index 0000000..6da6138 --- /dev/null +++ b/src/main/java/mvs/service/impl/SecurityServiceImpl.java @@ -0,0 +1,46 @@ +package mvs.service.impl; + +import mvs.service.SecurityService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; + +@Service +public class SecurityServiceImpl implements SecurityService { + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private UserDetailsService userDetailsService; + + private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class); + + @Override + public String findLoggedInUsername() { + Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails(); + if (userDetails instanceof UserDetails) { + return ((UserDetails)userDetails).getUsername(); + } + + return null; + } + + @Override + public void autologin(String username, String password) { + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); + + authenticationManager.authenticate(usernamePasswordAuthenticationToken); + + if (usernamePasswordAuthenticationToken.isAuthenticated()) { + SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); + logger.debug(String.format("Auto login %s successfully!", username)); + } + } +} diff --git a/src/main/java/mvs/service/impl/UserDetailsServiceImpl.java b/src/main/java/mvs/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..7f38a3d --- /dev/null +++ b/src/main/java/mvs/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,43 @@ +package mvs.service.impl; + +import mvs.model.Role; +import mvs.model.User; +import mvs.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Autowired + UserRepository userRepository; + @Override + @Transactional + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = userRepository.findByUsername(username); + if (user == null) { + throw new UsernameNotFoundException("User not found"); + } + + Set grantedAuthorities = new HashSet(); + Set roles = user.getRoles(); + for (Role role : roles) { + grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); + } + + return new org.springframework.security.core.userdetails.User( + user.getUsername(), user.getPassword(), grantedAuthorities); + } + +} diff --git a/src/main/java/mvs/service/impl/UserServiceImpl.java b/src/main/java/mvs/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..b945afd --- /dev/null +++ b/src/main/java/mvs/service/impl/UserServiceImpl.java @@ -0,0 +1,52 @@ +package mvs.service.impl; + + +import mvs.model.Role; +import mvs.model.User; +import mvs.repository.RoleRepository; +import mvs.repository.UserRepository; +import mvs.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashSet; + +@Service +public class UserServiceImpl implements UserService { + @Autowired + private UserRepository userRepository; + @Autowired + private RoleRepository roleRepository; + @Autowired + private BCryptPasswordEncoder bCryptPasswordEncoder; + + @Override + public void save(User user) { + userRepository.save(user); + } + + @Override + public void saveAndGrantRole(User user) { + user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); + HashSet roles = new HashSet(); + roles.add(roleRepository.findByName("ROLE_MEMBER")); + user.setRoles(roles); + userRepository.save(user); + + } + + + @Override + public User findByUsername(String username) { + return userRepository.findByUsername(username); + } + + @Override + public User getCurentUser() { + User user=userRepository.findByUsername( SecurityContextHolder.getContext().getAuthentication().getName() ); + + return user; + } +} diff --git a/src/main/java/mvs/validator/UserValidator.java b/src/main/java/mvs/validator/UserValidator.java new file mode 100644 index 0000000..21eaf6b --- /dev/null +++ b/src/main/java/mvs/validator/UserValidator.java @@ -0,0 +1,43 @@ +package mvs.validator; + + +import mvs.model.User; +import mvs.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +@Component +public class UserValidator implements Validator { + @Autowired + private UserService userService; + + @Override + public boolean supports(Class aClass) { + return User.class.equals(aClass); + } + + @Override + public void validate(Object o, Errors errors) { + User user = (User) o; + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "NotEmpty"); + if (user.getUsername().length() < 6 || user.getUsername().length() > 32) { + errors.rejectValue("username", "Size.user.username"); + } + if (userService.findByUsername(user.getUsername()) != null) { + errors.rejectValue("username", "Duplicate.user.username"); + } + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty"); + if (user.getPassword().length() < 8 || user.getPassword().length() > 32) { + errors.rejectValue("password", "Size.user.password"); + } + + if (!user.getPasswordConfirm().equals(user.getPassword())) { + errors.rejectValue("passwordConfirm", "Diff.user.passwordConfirm"); + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..32e9844 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,40 @@ +# =============================== +# THYMELEAF +# =============================== +spring.thymeleaf.cache=false + +# =============================== +# DATASOURCE +# =============================== + +# Set here configurations for the database connection +spring.http.multipart.max-file-size=2MB +spring.http.multipart.max-request-size=2MB +spring.datasource.url=jdbc:mysql://localhost:3306/thym?autoReconnect=true&characterEncoding=utf8&useUnicode=true&connectionCollation=utf8_general_ci&characterSetResults=utf8 +spring.datasource.username=root +spring.datasource.password= +image.source=/home/quangminh/IdeaProjects/demo-thym/src/main/resources/static/images/ +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +server.port=8080 + +# =============================== +# JPA / HIBERNATE +# =============================== + +# Use spring.jpa.properties.* for Hibernate native properties (the prefix is +# stripped before adding them to the entity manager). + +# Show or not log for each sql query +spring.jpa.show-sql=true + +# Hibernate ddl auto (create, create-drop, update): with "update" the database +# schema will be automatically updated accordingly to java entities found in +# the project +spring.jpa.hibernate.ddl-auto=update + +# Naming strategy +spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy + +# Allows Hibernate to generate SQL optimized for a particular DBMS +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect + diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000..9e593d1 --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,7 @@ +NotEmpty=This field is required. +Size.user.username=Please use between 6 and 32 characters. +Duplicate.user.username=Someone already has that username. +Size.user.password=Try one with at least 8 characters. +Diff.user.passwordConfirm=These passwords don't match. +NotEmpty.contact.name=This field is required. +Email.contact.email=Vui l\u00f2ng nh\u1eadp \u0111\u00fang \u0111\u1ecbnh d\u1ea1ng email diff --git a/src/main/resources/static/css/image.css b/src/main/resources/static/css/image.css new file mode 100644 index 0000000..2fff339 --- /dev/null +++ b/src/main/resources/static/css/image.css @@ -0,0 +1,22 @@ +.circle-img { + width: 200px; + height: 200px; + overflow: hidden; +} +.circle-img-mini { + width: 120px; + height: 120px; + overflow: hidden; +} + +.circle-img img { + height: 100%; + transform: translateX(-50%); + margin-left: 50%; +} + +.circle-img-mini img { + height: 100%; + transform: translateX(-50%); + margin-left: 50%; +} diff --git a/src/main/resources/static/css/index.css b/src/main/resources/static/css/index.css new file mode 100644 index 0000000..2c290b2 --- /dev/null +++ b/src/main/resources/static/css/index.css @@ -0,0 +1,31 @@ + +ul.listrap{ + list-style-type: none; +} + +.listrap > li{ + padding: 12px; + border-bottom: 1px solid #f3f3f3; +} + +.listrap .listrap-toggle{ + display: inline-block; + width: 100%; +} + +.listrap .listrap-toggle .ava{ + display: inline-block; + width: 8%; + float: left; +} + +.listrap .listrap-toggle .content{ + display: inline-block; + width: 90%; + float: left; +} + +.listrap-toggle .content .title{ + font-size: 1.1em; + font-weight: bold; +} \ No newline at end of file diff --git a/src/main/resources/static/css/layout.css b/src/main/resources/static/css/layout.css new file mode 100644 index 0000000..2a0f6ea --- /dev/null +++ b/src/main/resources/static/css/layout.css @@ -0,0 +1,45 @@ +nav{ + display: inline-block; +} +.navbar-wrapper { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 20; +} + /* Flip around the padding for proper display in narrow viewports */ +.navbar-wrapper .container { + padding-left: 0; + padding-right: 0; + width: 100%; +} +.navbar-wrapper .navbar { + padding-left: 15px; + padding-right: 15px; +} + +.navbar-content +{ + width:320px; + padding: 15px; + padding-bottom:0px; +} +.navbar-content:before, .navbar-content:after +{ + display: table; + content: ""; + line-height: 0; +} +.navbar-nav.navbar-right:last-child { + margin-right: 15px !important; +} +.navbar-footer +{ + background-color:#DDD; +} +.navbar-footer-content { padding:15px 15px 15px 15px; } +.dropdown-menu { + padding: 0px; + overflow: hidden; +} \ No newline at end of file diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css new file mode 100644 index 0000000..981b6b9 --- /dev/null +++ b/src/main/resources/static/css/style.css @@ -0,0 +1,69 @@ +.main-content { + min-height: 500px; + max-width: 700px; + margin-top: 70px; +} + +.list { + max-width: 800px; +} + +.form { + max-width: 450px; +} + +.row { + margin-top: 30px; +} + +.table th, td { + text-align: center; +} + +.field-error { + border: 1px solid #ff0000; + margin-bottom: 10px; +} + +.dropbtn { + background-color: #4CAF50; + color: white; + padding: 16px; + font-size: 16px; + border: none; + cursor: pointer; +} + +.dropbtn:hover, .dropbtn:focus { + background-color: #3e8e41; +} + +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown-content { + display: none; + position: absolute; + background-color: #f9f9f9; + min-width: 160px; + overflow: auto; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; +} + +.dropdown-content a { + color: black; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown a:hover {background-color: #f1f1f1} + +.show {display:block;} + +.marg { + margin-right :15px; +} \ No newline at end of file diff --git a/src/main/resources/static/images/13224234_590858354408064_771242502_o.png b/src/main/resources/static/images/13224234_590858354408064_771242502_o.png new file mode 100644 index 0000000..4c00a0e Binary files /dev/null and b/src/main/resources/static/images/13224234_590858354408064_771242502_o.png differ diff --git a/src/main/resources/static/images/15134488_629731210485537_2024972510_n.jpg b/src/main/resources/static/images/15134488_629731210485537_2024972510_n.jpg new file mode 100644 index 0000000..621d781 Binary files /dev/null and b/src/main/resources/static/images/15134488_629731210485537_2024972510_n.jpg differ diff --git a/src/main/resources/static/images/15242020_1551825914834001_8808148744000911046_n.jpg b/src/main/resources/static/images/15242020_1551825914834001_8808148744000911046_n.jpg new file mode 100644 index 0000000..1f9af22 Binary files /dev/null and b/src/main/resources/static/images/15242020_1551825914834001_8808148744000911046_n.jpg differ diff --git a/src/main/resources/static/images/DSC_1107.JPG b/src/main/resources/static/images/DSC_1107.JPG new file mode 100644 index 0000000..596538b Binary files /dev/null and b/src/main/resources/static/images/DSC_1107.JPG differ diff --git a/src/main/resources/static/images/inu.jpg b/src/main/resources/static/images/inu.jpg new file mode 100644 index 0000000..1687c4f Binary files /dev/null and b/src/main/resources/static/images/inu.jpg differ diff --git a/src/main/resources/static/images/logo-2.png b/src/main/resources/static/images/logo-2.png new file mode 100644 index 0000000..5290d05 Binary files /dev/null and b/src/main/resources/static/images/logo-2.png differ diff --git a/src/main/resources/static/images/logo.png b/src/main/resources/static/images/logo.png new file mode 100644 index 0000000..1c649a3 Binary files /dev/null and b/src/main/resources/static/images/logo.png differ diff --git a/src/main/resources/static/js/thumbnail.js b/src/main/resources/static/js/thumbnail.js new file mode 100644 index 0000000..85e8f0a --- /dev/null +++ b/src/main/resources/static/js/thumbnail.js @@ -0,0 +1,31 @@ +/** + * Created by quangminh on 13/07/2017. + */ +// Licenced under MIT License +// For updates, improvements and issues, see https://github.com/inosoftbr/listrap + +jQuery.fn.extend({ + listrap: function () { + var listrap = this; + listrap.getSelection = function () { + var selection = new Array(); + listrap.children("li.active").each(function (ix, el) { + selection.push($(el)[0]); + }); + return selection; + } + var toggle = "li .listrap-toggle "; + var selectionChanged = function() { + $(this).parent().parent().toggleClass("active"); + listrap.trigger("selection-changed", [listrap.getSelection()]); + } + $(listrap).find(toggle + "img").on("click", selectionChanged); + $(listrap).find(toggle + "span").on("click", selectionChanged); + return listrap; + } +}); +$(document).ready(function () { + $(".listrap").listrap().on("selection-changed", function (event, selection) { + console.log(selection); + }); +}); \ No newline at end of file diff --git a/src/main/resources/static/js/upload.js b/src/main/resources/static/js/upload.js new file mode 100644 index 0000000..42f9a58 --- /dev/null +++ b/src/main/resources/static/js/upload.js @@ -0,0 +1,57 @@ +/** + * Created by quangminh on 07/07/2017. + */ + + +$(document).ready(function () { + + $("#btnSubmit").click(function (event) { + + //stop submit the form, we will post it manually. + event.preventDefault(); + + fire_ajax_submit(); + + }); + +}); + +function fire_ajax_submit() { + + // Get form + var form = $('#fileUploadForm')[0]; + + var data = new FormData(form); + + data.append("CustomField", "This is some extra data, testing"); + + $("#btnSubmit").prop("disabled", true); + + $.ajax({ + type: "POST", + enctype: 'multipart/form-data', + url: "/api/upload/multi", + data: data, + //http://api.jquery.com/jQuery.ajax/ + //https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects + processData: false, //prevent jQuery from automatically transforming the data into a query string + contentType: false, + cache: false, + timeout: 600000, + success: function (data) { + + $("#result").text(data); + console.log("SUCCESS : ", data); + $("#btnSubmit").prop("disabled", false); + + }, + error: function (e) { + + $("#result").text(e.responseText); + console.log("ERROR : ", e); + $("#btnSubmit").prop("disabled", false); + + } + }); + +} diff --git a/src/main/resources/templates/admin/admin.html b/src/main/resources/templates/admin/admin.html new file mode 100644 index 0000000..aa8b3b1 --- /dev/null +++ b/src/main/resources/templates/admin/admin.html @@ -0,0 +1,10 @@ + + + + +Admin Page + + +

Welcome, Admin

+ + \ No newline at end of file diff --git a/src/main/resources/templates/error/403.html b/src/main/resources/templates/error/403.html new file mode 100644 index 0000000..0adbd81 --- /dev/null +++ b/src/main/resources/templates/error/403.html @@ -0,0 +1,10 @@ + + + + +403 Page + + +

403 Error: Access denied

+ + \ No newline at end of file diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html new file mode 100644 index 0000000..f54028f --- /dev/null +++ b/src/main/resources/templates/layout.html @@ -0,0 +1,147 @@ + + + + + + + + + Spring MyContact + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Main content

+ + +
+ © 2017 DAKA +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/post/list_post.html b/src/main/resources/templates/post/list_post.html new file mode 100644 index 0000000..60ab761 --- /dev/null +++ b/src/main/resources/templates/post/list_post.html @@ -0,0 +1,86 @@ + + + + + Index Page + + + + + + +
+ +
+
    +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/post/show_post.html b/src/main/resources/templates/post/show_post.html new file mode 100644 index 0000000..fe52c16 --- /dev/null +++ b/src/main/resources/templates/post/show_post.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/post/tinymce.html b/src/main/resources/templates/post/tinymce.html new file mode 100644 index 0000000..f528e8d --- /dev/null +++ b/src/main/resources/templates/post/tinymce.html @@ -0,0 +1,82 @@ + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+ +
+ + + + + + + + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + +
+
+
+
+
+ + + + + diff --git a/src/main/resources/templates/profile/profile.html b/src/main/resources/templates/profile/profile.html new file mode 100644 index 0000000..f5b95ef --- /dev/null +++ b/src/main/resources/templates/profile/profile.html @@ -0,0 +1,102 @@ + + + + + +
+ +

Edit Profile

+
+ +
+
+ + + avatar + + + avatar + + +
Upload a different photo...
+ +
+
+ + + + +
+
+ × + + This is an .alert. Use this to show important messages to the user. +
+

Personal info

+
+ + + + + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+ +
+ + +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/security/login.html b/src/main/resources/templates/security/login.html new file mode 100644 index 0000000..0bacf49 --- /dev/null +++ b/src/main/resources/templates/security/login.html @@ -0,0 +1,61 @@ + + + + + Login Page + + + + + +
+

Invalid email or password

+

You have been logged out

+
+
+
Sign In
+ +
+ +
+ + + +
+ +
+ + +
+ +
+ + +
+
+ + +
+ + Login with Facebook + +
+
+ + +
+
+
+ Don't have an account! + + Sign Up Here + +
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/security/register.html b/src/main/resources/templates/security/register.html new file mode 100644 index 0000000..7f5bdf4 --- /dev/null +++ b/src/main/resources/templates/security/register.html @@ -0,0 +1,83 @@ + + + + + + +
+
+
+
Sign Up
+ +
+
+
+ + + + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ +
+ + or +
+
+ +
+ +
+ +
+ +
+ + + +
+
+
+ + + + +
+ + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/upload.html b/src/main/resources/templates/upload.html new file mode 100644 index 0000000..7d5dbd3 --- /dev/null +++ b/src/main/resources/templates/upload.html @@ -0,0 +1,13 @@ + + + + +

Spring Boot file upload example

+ +
+

+ +
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/uploadStatus.html b/src/main/resources/templates/uploadStatus.html new file mode 100644 index 0000000..71fb76d --- /dev/null +++ b/src/main/resources/templates/uploadStatus.html @@ -0,0 +1,12 @@ + + + + +

Spring Boot - Upload Status

+ +
+

+

+ + + \ No newline at end of file diff --git a/target/classes/application.properties b/target/classes/application.properties new file mode 100644 index 0000000..32e9844 --- /dev/null +++ b/target/classes/application.properties @@ -0,0 +1,40 @@ +# =============================== +# THYMELEAF +# =============================== +spring.thymeleaf.cache=false + +# =============================== +# DATASOURCE +# =============================== + +# Set here configurations for the database connection +spring.http.multipart.max-file-size=2MB +spring.http.multipart.max-request-size=2MB +spring.datasource.url=jdbc:mysql://localhost:3306/thym?autoReconnect=true&characterEncoding=utf8&useUnicode=true&connectionCollation=utf8_general_ci&characterSetResults=utf8 +spring.datasource.username=root +spring.datasource.password= +image.source=/home/quangminh/IdeaProjects/demo-thym/src/main/resources/static/images/ +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +server.port=8080 + +# =============================== +# JPA / HIBERNATE +# =============================== + +# Use spring.jpa.properties.* for Hibernate native properties (the prefix is +# stripped before adding them to the entity manager). + +# Show or not log for each sql query +spring.jpa.show-sql=true + +# Hibernate ddl auto (create, create-drop, update): with "update" the database +# schema will be automatically updated accordingly to java entities found in +# the project +spring.jpa.hibernate.ddl-auto=update + +# Naming strategy +spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy + +# Allows Hibernate to generate SQL optimized for a particular DBMS +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect + diff --git a/target/classes/messages.properties b/target/classes/messages.properties new file mode 100644 index 0000000..9e593d1 --- /dev/null +++ b/target/classes/messages.properties @@ -0,0 +1,7 @@ +NotEmpty=This field is required. +Size.user.username=Please use between 6 and 32 characters. +Duplicate.user.username=Someone already has that username. +Size.user.password=Try one with at least 8 characters. +Diff.user.passwordConfirm=These passwords don't match. +NotEmpty.contact.name=This field is required. +Email.contact.email=Vui l\u00f2ng nh\u1eadp \u0111\u00fang \u0111\u1ecbnh d\u1ea1ng email diff --git a/target/classes/static/css/image.css b/target/classes/static/css/image.css new file mode 100644 index 0000000..2fff339 --- /dev/null +++ b/target/classes/static/css/image.css @@ -0,0 +1,22 @@ +.circle-img { + width: 200px; + height: 200px; + overflow: hidden; +} +.circle-img-mini { + width: 120px; + height: 120px; + overflow: hidden; +} + +.circle-img img { + height: 100%; + transform: translateX(-50%); + margin-left: 50%; +} + +.circle-img-mini img { + height: 100%; + transform: translateX(-50%); + margin-left: 50%; +} diff --git a/target/classes/static/css/index.css b/target/classes/static/css/index.css new file mode 100644 index 0000000..2c290b2 --- /dev/null +++ b/target/classes/static/css/index.css @@ -0,0 +1,31 @@ + +ul.listrap{ + list-style-type: none; +} + +.listrap > li{ + padding: 12px; + border-bottom: 1px solid #f3f3f3; +} + +.listrap .listrap-toggle{ + display: inline-block; + width: 100%; +} + +.listrap .listrap-toggle .ava{ + display: inline-block; + width: 8%; + float: left; +} + +.listrap .listrap-toggle .content{ + display: inline-block; + width: 90%; + float: left; +} + +.listrap-toggle .content .title{ + font-size: 1.1em; + font-weight: bold; +} \ No newline at end of file diff --git a/target/classes/static/css/layout.css b/target/classes/static/css/layout.css new file mode 100644 index 0000000..2a0f6ea --- /dev/null +++ b/target/classes/static/css/layout.css @@ -0,0 +1,45 @@ +nav{ + display: inline-block; +} +.navbar-wrapper { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 20; +} + /* Flip around the padding for proper display in narrow viewports */ +.navbar-wrapper .container { + padding-left: 0; + padding-right: 0; + width: 100%; +} +.navbar-wrapper .navbar { + padding-left: 15px; + padding-right: 15px; +} + +.navbar-content +{ + width:320px; + padding: 15px; + padding-bottom:0px; +} +.navbar-content:before, .navbar-content:after +{ + display: table; + content: ""; + line-height: 0; +} +.navbar-nav.navbar-right:last-child { + margin-right: 15px !important; +} +.navbar-footer +{ + background-color:#DDD; +} +.navbar-footer-content { padding:15px 15px 15px 15px; } +.dropdown-menu { + padding: 0px; + overflow: hidden; +} \ No newline at end of file diff --git a/target/classes/static/css/style.css b/target/classes/static/css/style.css new file mode 100644 index 0000000..981b6b9 --- /dev/null +++ b/target/classes/static/css/style.css @@ -0,0 +1,69 @@ +.main-content { + min-height: 500px; + max-width: 700px; + margin-top: 70px; +} + +.list { + max-width: 800px; +} + +.form { + max-width: 450px; +} + +.row { + margin-top: 30px; +} + +.table th, td { + text-align: center; +} + +.field-error { + border: 1px solid #ff0000; + margin-bottom: 10px; +} + +.dropbtn { + background-color: #4CAF50; + color: white; + padding: 16px; + font-size: 16px; + border: none; + cursor: pointer; +} + +.dropbtn:hover, .dropbtn:focus { + background-color: #3e8e41; +} + +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown-content { + display: none; + position: absolute; + background-color: #f9f9f9; + min-width: 160px; + overflow: auto; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; +} + +.dropdown-content a { + color: black; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown a:hover {background-color: #f1f1f1} + +.show {display:block;} + +.marg { + margin-right :15px; +} \ No newline at end of file diff --git a/target/classes/static/images/13224234_590858354408064_771242502_o.png b/target/classes/static/images/13224234_590858354408064_771242502_o.png new file mode 100644 index 0000000..4c00a0e Binary files /dev/null and b/target/classes/static/images/13224234_590858354408064_771242502_o.png differ diff --git a/target/classes/static/images/15134488_629731210485537_2024972510_n.jpg b/target/classes/static/images/15134488_629731210485537_2024972510_n.jpg new file mode 100644 index 0000000..621d781 Binary files /dev/null and b/target/classes/static/images/15134488_629731210485537_2024972510_n.jpg differ diff --git a/target/classes/static/images/15242020_1551825914834001_8808148744000911046_n.jpg b/target/classes/static/images/15242020_1551825914834001_8808148744000911046_n.jpg new file mode 100644 index 0000000..1f9af22 Binary files /dev/null and b/target/classes/static/images/15242020_1551825914834001_8808148744000911046_n.jpg differ diff --git a/target/classes/static/images/DSC_1107.JPG b/target/classes/static/images/DSC_1107.JPG new file mode 100644 index 0000000..596538b Binary files /dev/null and b/target/classes/static/images/DSC_1107.JPG differ diff --git a/target/classes/static/images/inu.jpg b/target/classes/static/images/inu.jpg new file mode 100644 index 0000000..1687c4f Binary files /dev/null and b/target/classes/static/images/inu.jpg differ diff --git a/target/classes/static/images/logo-2.png b/target/classes/static/images/logo-2.png new file mode 100644 index 0000000..5290d05 Binary files /dev/null and b/target/classes/static/images/logo-2.png differ diff --git a/target/classes/static/images/logo.png b/target/classes/static/images/logo.png new file mode 100644 index 0000000..1c649a3 Binary files /dev/null and b/target/classes/static/images/logo.png differ diff --git a/target/classes/static/js/thumbnail.js b/target/classes/static/js/thumbnail.js new file mode 100644 index 0000000..85e8f0a --- /dev/null +++ b/target/classes/static/js/thumbnail.js @@ -0,0 +1,31 @@ +/** + * Created by quangminh on 13/07/2017. + */ +// Licenced under MIT License +// For updates, improvements and issues, see https://github.com/inosoftbr/listrap + +jQuery.fn.extend({ + listrap: function () { + var listrap = this; + listrap.getSelection = function () { + var selection = new Array(); + listrap.children("li.active").each(function (ix, el) { + selection.push($(el)[0]); + }); + return selection; + } + var toggle = "li .listrap-toggle "; + var selectionChanged = function() { + $(this).parent().parent().toggleClass("active"); + listrap.trigger("selection-changed", [listrap.getSelection()]); + } + $(listrap).find(toggle + "img").on("click", selectionChanged); + $(listrap).find(toggle + "span").on("click", selectionChanged); + return listrap; + } +}); +$(document).ready(function () { + $(".listrap").listrap().on("selection-changed", function (event, selection) { + console.log(selection); + }); +}); \ No newline at end of file diff --git a/target/classes/static/js/upload.js b/target/classes/static/js/upload.js new file mode 100644 index 0000000..42f9a58 --- /dev/null +++ b/target/classes/static/js/upload.js @@ -0,0 +1,57 @@ +/** + * Created by quangminh on 07/07/2017. + */ + + +$(document).ready(function () { + + $("#btnSubmit").click(function (event) { + + //stop submit the form, we will post it manually. + event.preventDefault(); + + fire_ajax_submit(); + + }); + +}); + +function fire_ajax_submit() { + + // Get form + var form = $('#fileUploadForm')[0]; + + var data = new FormData(form); + + data.append("CustomField", "This is some extra data, testing"); + + $("#btnSubmit").prop("disabled", true); + + $.ajax({ + type: "POST", + enctype: 'multipart/form-data', + url: "/api/upload/multi", + data: data, + //http://api.jquery.com/jQuery.ajax/ + //https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects + processData: false, //prevent jQuery from automatically transforming the data into a query string + contentType: false, + cache: false, + timeout: 600000, + success: function (data) { + + $("#result").text(data); + console.log("SUCCESS : ", data); + $("#btnSubmit").prop("disabled", false); + + }, + error: function (e) { + + $("#result").text(e.responseText); + console.log("ERROR : ", e); + $("#btnSubmit").prop("disabled", false); + + } + }); + +} diff --git a/target/classes/templates/admin/admin.html b/target/classes/templates/admin/admin.html new file mode 100644 index 0000000..aa8b3b1 --- /dev/null +++ b/target/classes/templates/admin/admin.html @@ -0,0 +1,10 @@ + + + + +Admin Page + + +

Welcome, Admin

+ + \ No newline at end of file diff --git a/target/classes/templates/error/403.html b/target/classes/templates/error/403.html new file mode 100644 index 0000000..0adbd81 --- /dev/null +++ b/target/classes/templates/error/403.html @@ -0,0 +1,10 @@ + + + + +403 Page + + +

403 Error: Access denied

+ + \ No newline at end of file diff --git a/target/classes/templates/layout.html b/target/classes/templates/layout.html new file mode 100644 index 0000000..f54028f --- /dev/null +++ b/target/classes/templates/layout.html @@ -0,0 +1,147 @@ + + + + + + + + + Spring MyContact + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Main content

+ + +
+ © 2017 DAKA +
+ + \ No newline at end of file diff --git a/target/classes/templates/post/list_post.html b/target/classes/templates/post/list_post.html new file mode 100644 index 0000000..60ab761 --- /dev/null +++ b/target/classes/templates/post/list_post.html @@ -0,0 +1,86 @@ + + + + + Index Page + + + + + + +
+ +
+
    +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
  • +
    +
    + +
    +
    +

    1231231321321321

    +

    asdsfsdfsdfsadfas

    +
    dffdsafsaf
    +
    + +
    +
  • +
+
+
+ + \ No newline at end of file diff --git a/target/classes/templates/post/show_post.html b/target/classes/templates/post/show_post.html new file mode 100644 index 0000000..fe52c16 --- /dev/null +++ b/target/classes/templates/post/show_post.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/target/classes/templates/post/tinymce.html b/target/classes/templates/post/tinymce.html new file mode 100644 index 0000000..f528e8d --- /dev/null +++ b/target/classes/templates/post/tinymce.html @@ -0,0 +1,82 @@ + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+ +
+ + + + + + + + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + +
+
+
+
+
+ + + + + diff --git a/target/classes/templates/profile/profile.html b/target/classes/templates/profile/profile.html new file mode 100644 index 0000000..f5b95ef --- /dev/null +++ b/target/classes/templates/profile/profile.html @@ -0,0 +1,102 @@ + + + + + +
+ +

Edit Profile

+
+ +
+
+ + + avatar + + + avatar + + +
Upload a different photo...
+ +
+
+ + + + +
+
+ × + + This is an .alert. Use this to show important messages to the user. +
+

Personal info

+
+ + + + + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+ +
+ + +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/target/classes/templates/security/login.html b/target/classes/templates/security/login.html new file mode 100644 index 0000000..0bacf49 --- /dev/null +++ b/target/classes/templates/security/login.html @@ -0,0 +1,61 @@ + + + + + Login Page + + + + + +
+

Invalid email or password

+

You have been logged out

+
+
+
Sign In
+ +
+ +
+ + + +
+ +
+ + +
+ +
+ + +
+
+ + +
+ + Login with Facebook + +
+
+ + +
+
+
+ Don't have an account! + + Sign Up Here + +
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/target/classes/templates/security/register.html b/target/classes/templates/security/register.html new file mode 100644 index 0000000..7f5bdf4 --- /dev/null +++ b/target/classes/templates/security/register.html @@ -0,0 +1,83 @@ + + + + + + +
+
+
+
Sign Up
+ +
+
+
+ + + + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ +
+ + or +
+
+ +
+ +
+ +
+ +
+ + + +
+
+
+ + + + +
+ + + + + + + \ No newline at end of file diff --git a/target/classes/templates/upload.html b/target/classes/templates/upload.html new file mode 100644 index 0000000..7d5dbd3 --- /dev/null +++ b/target/classes/templates/upload.html @@ -0,0 +1,13 @@ + + + + +

Spring Boot file upload example

+ +
+

+ +
+ + + \ No newline at end of file diff --git a/target/classes/templates/uploadStatus.html b/target/classes/templates/uploadStatus.html new file mode 100644 index 0000000..71fb76d --- /dev/null +++ b/target/classes/templates/uploadStatus.html @@ -0,0 +1,12 @@ + + + + +

Spring Boot - Upload Status

+ +
+

+

+ + + \ No newline at end of file