Compare commits

..

No commits in common. "794388d0f63f189e10cf067878cbf82d070b17d0" and "8ae10007bb0cbdae3547c8d519b13a6faeea2d2e" have entirely different histories.

39 changed files with 3866 additions and 1873 deletions

Binary file not shown.

3828
data.trace.db Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,5 @@
package com.webproglabs.lab1; package com.webproglabs.lab1;
import com.webproglabs.lab1.models.UserRole;
import com.webproglabs.lab1.mvc.UserSignupMvcController;
import com.webproglabs.lab1.services.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -32,47 +29,47 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
private static final String LOGIN_URL = "/login"; private static final String LOGIN_URL = "/login";
public static final String SPA_URL_MASK = "/{path:[^\\.]*}"; public static final String SPA_URL_MASK = "/{path:[^\\.]*}";
private final UserService userService; // private final ProfileService userService;
//
// public SecurityConfiguration(ProfileService userService) {
// this.userService = userService;
// createAdminOnStartup();
// }
public SecurityConfiguration(UserService userService) { // private void createAdminOnStartup() {
this.userService = userService; // final String admin = "admin";
createAdminOnStartup(); // if (userService.findByLogin(admin) == null) {
} // log.info("Admin user successfully created");
// try {
// userService.createUser(admin, admin, admin, UserRole.ADMIN);
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
// }
private void createAdminOnStartup() { // @Override
final String admin = "admin"; // protected void configure(HttpSecurity http) throws Exception {
if (userService.findUserByLogin(admin) == null) { //
log.info("Admin user successfully created"); // http.exceptionHandling().authenticationEntryPoint(delegatingEntryPoint());
try { // http.headers().frameOptions().sameOrigin().and()
userService.createUser(admin, admin, admin, UserRole.ADMIN); // .cors().and()
} catch (Exception e) { // .csrf().disable()
throw new RuntimeException(e); // .authorizeRequests()
} // .antMatchers(UserSignupMvcController.SIGNUP_URL).permitAll()
} // .antMatchers(HttpMethod.GET, LOGIN_URL).permitAll()
} // .anyRequest().authenticated()
// .and()
// .formLogin()
// .loginPage(LOGIN_URL).permitAll()
// .and()
// .logout().permitAll();
// }
@Override // @Override
protected void configure(HttpSecurity http) throws Exception { // protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(userService);
http.exceptionHandling().authenticationEntryPoint(delegatingEntryPoint()); // }
http.headers().frameOptions().sameOrigin().and()
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers(UserSignupMvcController.SIGNUP_URL).permitAll()
.antMatchers(HttpMethod.GET, LOGIN_URL).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage(LOGIN_URL).permitAll()
.and()
.logout().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override @Override
public void configure(WebSecurity web) { public void configure(WebSecurity web) {

View File

@ -1,12 +0,0 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByTextLike(String text);
Optional<Comment> findById(Long Id);
}

View File

@ -1,18 +0,0 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByTextLike(String text);
Optional<Post> findById(Long Id);
@Query("SELECT p FROM Post p WHERE p.topic = :topic AND p.text LIKE %:text%")
List<Post> findPostsByTextInTopic(@Param("text") String text, @Param("topic") Topic topic);
}

View File

@ -1,15 +0,0 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface TopicRepository extends JpaRepository<Topic, Long> {
Optional<Topic> findById(Long Id);
}

View File

@ -1,12 +0,0 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findOneByLoginIgnoreCase(String login);
Optional<User> findById(Long Id);
}

View File

@ -1,30 +0,0 @@
package com.webproglabs.lab1.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.webproglabs.lab1.models.Comment;
import javax.validation.constraints.NotBlank;
public class CommentDto {
private Long id;
@NotBlank
private String text;
@NotBlank
private String authorLogin;
public CommentDto(Comment comment) {
this.id = comment.getId();
this.text = comment.getText();
this.authorLogin = comment.getOwner().getLogin();
}
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public long getId() {
return id;
}
public String getText() {return text;}
public void setText(String text) {
this.text = text;
}
public String getAuthor() {return authorLogin;}
}

View File

@ -1,41 +0,0 @@
package com.webproglabs.lab1.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
public class PostDto {
private Long id;
@NotBlank
private String text;
@NotNull
private List<CommentDto> comments = new ArrayList<>();
@NotBlank
private String authorLogin;
public PostDto(Post post){
this.id = post.getId();
this.text = post.getText();
for(Comment comment: post.getComments()){
comments.add(new CommentDto(comment));
}
this.authorLogin = post.getAuthor().getLogin();
}
public PostDto(){}
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public long getId() {
return id;
}
public String getText() {return text;}
public void setText(String text) {
this.text = text;
}
public List<CommentDto> getComments() {return comments;}
public String getAuthor() {return authorLogin;}
}

View File

@ -1,54 +0,0 @@
package com.webproglabs.lab1.dto;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
public class TopicDto {
private Long id;
@NotBlank
private String name;
@NotBlank
private String description;
@NotNull
private List<PostDto> posts = new ArrayList<>();
public TopicDto(Topic topic) {
this.id = topic.getId();
this.name = topic.getName();
this.description = topic.getDescription();
for(Post post: topic.getPosts()){
posts.add(new PostDto(post));
}
}
public TopicDto(){}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<PostDto> getPosts() {
return posts;
}
public void setName(String name) {
this.name = name;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -1,55 +0,0 @@
package com.webproglabs.lab1.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.models.UserRole;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
public class UserDto {
private Long id;
@NotBlank
private String login;
@NotBlank
private String password;
@NotNull
private List<CommentDto> comments = new ArrayList<>();
@NotNull
private List<PostDto> posts = new ArrayList<>();
@NotNull
private UserRole role;
public UserDto(){}
public UserDto(User user){
this.id = user.getId();
this.login = user.getLogin();
this.password = user.getPassword();
this.role = user.getRole();
for(Comment comment: user.getComments()){
comments.add(new CommentDto(comment));
}
for (Post post: user.getPosts()) {
posts.add(new PostDto(post));
}
}
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public long getId() {
return id;
}
public String getLogin() {return login;}
public void setLogin(String login) {this.login = login;}
public String getPassword() {return password;}
public UserRole getRole() {return role;}
public void setPassword(String password) {this.password = password;}
public List<CommentDto> getComments() {return comments;}
public List<PostDto> getPosts() {return posts;}
}

View File

@ -1,40 +0,0 @@
package com.webproglabs.lab1.dto;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
public class UserSignupDto {
@NotBlank
@Size(min = 4, max = 64)
private String login;
@NotBlank
@Size(min = 4, max = 64)
private String password;
@NotBlank
@Size(min = 4, max = 64)
private String passwordConfirm;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPasswordConfirm() {
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}
}

View File

@ -1,82 +0,0 @@
package com.webproglabs.lab1.models;
import javax.persistence.*;
import java.util.Objects;
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String text;
@ManyToOne()
private User owner;
@ManyToOne()
private Post post;
public Comment(){};
public Comment(String text, User owner, Post post) {
this.text = text;
this.owner = owner;
this.post = post;
}
public Long getId() {
return id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner.getComments().remove(this);
this.owner = owner;
if (!owner.getComments().contains(this)) {
owner.getComments().add(this);
}
}
public Post getPost() {return post; }
public void setPost(Post post) {
this.post.getComments().remove(this);
this.post = post;
if (!post.getComments().contains(this)) {
post.getComments().add(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Comment comment = (Comment) o;
return Objects.equals(id, comment.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public String ToString() {
return "Comment{" +
"id=" + id +
", text='" + text + '\'' +
", owner='" + owner.ToString() + '\'' +
", post='" + post.ToString() + '\'' +
'}';
}
}

View File

@ -1,97 +0,0 @@
package com.webproglabs.lab1.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String text;
@OneToMany(mappedBy = "post", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Comment> comments = new ArrayList<Comment>();
@ManyToOne()
private User author;
@ManyToOne()
private Topic topic;
public Post(){}
public Post(String text, User author, Topic topic) {
this.text = text;
this.author = author;
this.topic = topic;
}
public Long getId() {
return id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author.getPosts().remove(this);
this.author = author;
if (!author.getPosts().contains(this)) {
author.getPosts().add(this);
}
}
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic.getPosts().remove(this);
this.topic = topic;
if (!topic.getPosts().contains(this)) {
topic.getPosts().add(this);
}
}
public List<Comment> getComments() { return comments; }
public void addComment(Comment comment) {
this.comments.add(comment);
if (comment.getPost() != this) {
comment.setPost(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Post post = (Post) o;
return Objects.equals(id, post.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public String ToString() {
return "Post{" +
"id=" + id +
", text='" + text + '\'' +
", author='" + author.ToString() + '\'' +
'}';
}
}

View File

@ -1,78 +0,0 @@
package com.webproglabs.lab1.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
public class Topic {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@OneToMany(mappedBy = "topic", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Post> posts;
public Topic(String name, String description) {
this.name = name;
this.description = description;
this.posts = new ArrayList<Post>();
}
public Topic(){}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<Post> getPosts() {
return posts;
}
public void setName(String name) {
this.name = name;
}
public void setDescription(String description) {
this.description = description;
}
public void addPost(Post post) {
this.posts.add(post);
if (post.getTopic() != this) {
post.setTopic(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Topic topic = (Topic) o;
return Objects.equals(id, topic.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public String ToString() {
return "Topic{" +
"id=" + id +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@ -1,110 +0,0 @@
package com.webproglabs.lab1.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
@Table(name="users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, unique = true, length = 64)
@NotBlank
@Size(min = 3, max = 64)
private String login;
@Column(nullable = false, length = 64)
@NotBlank
@Size(min = 4, max = 64)
private String password;
private UserRole role;
@OneToMany(mappedBy = "owner", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Comment> comments = new ArrayList<Comment>();
@OneToMany(mappedBy = "author", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Post> posts = new ArrayList<Post>();
public User(){}
public User(String login, String password) {
this.login = login;
this.password=password;
this.role = UserRole.USER;
};
public User(String login, String password, UserRole role) {
this.login = login;
this.password=password;
this.role = role;
};
public Long getId() {
return id;
}
public String getLogin() {
return login;
}
public UserRole getRole() {
return role;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Comment> getComments() { return comments; }
public List<Post> getPosts() {return posts; }
public void addComment(Comment comment) {
this.comments.add(comment);
if (comment.getOwner() != this) {
comment.setOwner(this);
}
}
public void addPost(Post post) {
this.posts.add(post);
if (post.getAuthor() != this) {
post.setAuthor(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public String ToString() {
return "User{" +
"id=" + id +
", login='" + login + '\'' +
", password='" + password + '\'' +
'}';
}
}

View File

@ -1,20 +0,0 @@
package com.webproglabs.lab1.models;
import org.springframework.security.core.GrantedAuthority;
public enum UserRole implements GrantedAuthority {
ADMIN,
USER;
private static final String PREFIX = "ROLE_";
@Override
public String getAuthority() {
return PREFIX + this.name();
}
public static final class AsString {
public static final String ADMIN = PREFIX + "ADMIN";
public static final String USER = PREFIX + "USER";
}
}

View File

@ -1,142 +0,0 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.CommentDto;
import com.webproglabs.lab1.dto.PostDto;
import com.webproglabs.lab1.dto.TopicDto;
import com.webproglabs.lab1.dto.UserDto;
import com.webproglabs.lab1.services.CommentService;
import com.webproglabs.lab1.services.PostService;
import com.webproglabs.lab1.services.TopicService;
import com.webproglabs.lab1.services.UserService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/feed")
public class FeedMvcController {
private final UserService userService;
private final PostService postService;
private final CommentService commentService;
private final TopicService topicService;
public FeedMvcController(UserService userService, PostService postService, CommentService commentService, TopicService topicService) {
this.userService = userService;
this.postService = postService;
this.commentService = commentService;
this.topicService = topicService;
}
@GetMapping
public String getFeedPage(Model model) {
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
return "feed";
}
@GetMapping(value = {"/{id}"})
public String getFeedPageWithTopic(@PathVariable Long id, Model model) {
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(id).getPosts().stream().map(PostDto::new).toList());
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(id)));
return "feedPosts";
}
@GetMapping(value= {"/filter/{id}/"})
public String getFeedPageFiltered(@PathVariable Long id, @RequestParam(value="searchField") String searchField, Model model) {
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(id)));
model.addAttribute("posts", postService.findPostsInTopicByText(searchField, id).stream().map(PostDto::new).toList());
return "feedPosts";
}
@PostMapping(value={"/{topicId}/post/{id}/"})
public String createPost(@PathVariable Long topicId, @PathVariable Long id, @RequestParam(value="postInputField") String postInputField) {
postService.addPost(postInputField, id, topicId);
return "redirect:/feed/" + topicId.toString();
}
@PostMapping(value = {"/deletePost/{id}/{topicId}"})
public String deletePost(@PathVariable Long id, @PathVariable Long topicId) {
postService.deletePost(id);
return "redirect:/feed/" + topicId.toString();
}
@GetMapping(value = {"postModal/{id}/{topicId}"})
public String getPostEditModal(@PathVariable Long id,@PathVariable Long topicId, Model model) {
model.addAttribute("selectedPost", new PostDto(postService.findPostById(id)));
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(topicId).getPosts().stream().map(PostDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(topicId)));
return "editPostModal";
}
@PostMapping(value = {"editPost/{id}/{topicId}/"})
public String editPost(@PathVariable Long id, @PathVariable Long topicId, @RequestParam(value="postEditField") String postEditField) {
postService.updatePost(id, postEditField);
return "redirect:/feed/" + topicId.toString();
}
@GetMapping(value = {"commentModal/{topicId}/{postId}"})
public String getCommentModal(@PathVariable Long topicId,@PathVariable Long postId, Model model) {
model.addAttribute("selectedPost", new PostDto(postService.findPostById(postId)));
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(topicId).getPosts().stream().map(PostDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(topicId)));
return "commentModal";
}
@PostMapping(value = {"comment/{authorId}/{topicId}/{postId}/"})
public String createComment(@PathVariable Long authorId, @PathVariable Long topicId, @PathVariable Long postId, @RequestParam(value="commentInputField") String commentInputField) {
commentService.addComment(commentInputField, authorId, postId);
return "redirect:/feed/" + topicId.toString();
}
@PostMapping(value = {"/deleteComment/{id}/{topicId}"})
public String deleteComment(@PathVariable Long id, @PathVariable Long topicId) {
commentService.deleteComment(id);
return "redirect:/feed/" + topicId.toString();
}
@GetMapping(value = {"commentEditModal/{id}/{topicId}"})
public String getCommentEditModal(@PathVariable Long id,@PathVariable Long topicId, Model model) {
model.addAttribute("selectedComment", new CommentDto(commentService.findCommentById(id)));
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(topicId).getPosts().stream().map(PostDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(topicId)));
return "editCommentModal";
}
@PostMapping(value = {"editComment/{topicId}/{commentId}/"})
public String editComment(@PathVariable Long topicId,@PathVariable Long commentId, @RequestParam(value="commentEditField") String commentEditField) {
commentService.updateComment(commentId, commentEditField);
return "redirect:/feed/" + topicId.toString();
}
}

View File

@ -1,58 +0,0 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.TopicDto;
import com.webproglabs.lab1.dto.UserSignupDto;
import com.webproglabs.lab1.models.UserRole;
import com.webproglabs.lab1.services.TopicService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/topic")
public class TopicMvcController {
private final TopicService topicService;
public TopicMvcController(TopicService topicService) {
this.topicService = topicService;
}
@GetMapping
@Secured({UserRole.AsString.ADMIN})
public String getTopics(Model model) {
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
model.addAttribute("topicDto", new TopicDto());
return "topics";
}
@PostMapping(value = {"/create"})
@Secured({UserRole.AsString.ADMIN})
public String createTopic(@ModelAttribute TopicDto topicDto) {
topicService.addTopic(topicDto.getName(), topicDto.getDescription());
return "redirect:/topic";
}
@PostMapping(value = {"/delete/{Id}"})
@Secured({UserRole.AsString.ADMIN})
public String deleteTopic(@PathVariable Long Id) {
topicService.deleteTopic(Id);
return "redirect:/topic";
}
@GetMapping(value = {"/edit/{Id}"})
@Secured({UserRole.AsString.ADMIN})
public String getTopicEdit(@PathVariable Long Id, Model model) {
model.addAttribute("topic", new TopicDto(topicService.findTopicById(Id)));
model.addAttribute("topicDto", new TopicDto(topicService.findTopicById(Id)));
return "topicEdit";
}
@PostMapping(value = {"/edit/{Id}"})
@Secured({UserRole.AsString.ADMIN})
public String editTopic(@PathVariable Long Id, @ModelAttribute TopicDto topicDto) {
topicService.updateTopic(Id, topicDto.getName(), topicDto.getDescription());
return "redirect:/topic";
}
}

View File

@ -1,79 +0,0 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.UserDto;
import com.webproglabs.lab1.dto.UserSignupDto;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.models.UserRole;
import com.webproglabs.lab1.services.UserService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Controller
@RequestMapping("/users")
public class UserMvcController {
private final UserService userService;
public UserMvcController(UserService userService) {
this.userService = userService;
}
@GetMapping
@Secured({UserRole.AsString.ADMIN})
public String getUsersPage(Model model){
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
return "users";
}
@PostMapping(value = {"/{id}"})
@Secured({UserRole.AsString.ADMIN})
public String deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return "redirect:/users";
}
@GetMapping(value = {"/{login}"})
public String getUserPage(@PathVariable String login, Model model) {
model.addAttribute("user", new UserDto(userService.findUserByLogin(login)));
return "userPage";
}
@GetMapping(value = {"/settings"})
public String getUserEditPage(Model model) {
UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var currentUser = userService.findUserByLogin(principal.getUsername());
model.addAttribute("user", new UserDto(userService.findUserById(currentUser.getId())));
model.addAttribute("userDto", new UserSignupDto());
return "userEditPage";
}
@PostMapping(value = {"/edit/{id}"})
public String editUserData(@PathVariable Long id, @ModelAttribute("userDto") @Valid UserSignupDto userSignupDto,
BindingResult bindingResult,
Model model) {
UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var currentUser = userService.findUserByLogin(principal.getUsername());
model.addAttribute("user", new UserDto(userService.findUserById(currentUser.getId())));
model.addAttribute("userDto", new UserSignupDto());
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "userEditPage";
}
try {
final User user = userService.updateUser(id, userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
model.addAttribute("success", "Данные успешно изменены");
return "userEditPage";
} catch (Exception e) {
model.addAttribute("errors", e.getMessage());
return "userEditPage";
}
}
}

View File

@ -1,55 +0,0 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.UserSignupDto;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.services.UserService;
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.RequestMapping;
import javax.validation.Valid;
@Controller
@RequestMapping()
public class UserSignupMvcController {
public static final String SIGNUP_URL = "/signup";
private final UserService userService;
public UserSignupMvcController(UserService userService) {
this.userService = userService;
}
@GetMapping
public String toFeed() {
return "redirect:/feed";
}
@GetMapping(SIGNUP_URL)
public String showSignupForm(Model model) {
model.addAttribute("userDto", new UserSignupDto());
return "signup";
}
@PostMapping(SIGNUP_URL)
public String signup(@ModelAttribute("userDto") @Valid UserSignupDto userSignupDto,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "signup";
}
try {
final User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
return "redirect:/login?created=" + user.getLogin();
} catch (Exception e) {
model.addAttribute("errors", e.getMessage());
return "signup";
}
}
}

View File

@ -1,80 +0,0 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.CommentRepository;
import com.webproglabs.lab1.dao.PostRepository;
import com.webproglabs.lab1.dao.UserRepository;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.User;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class CommentService {
private final UserRepository userRepository;
private final CommentRepository commentRepository;
private final PostRepository postRepository;
public CommentService(UserRepository profileRepository, CommentRepository commentRepository, PostRepository postRepository) {
this.userRepository = profileRepository;
this.commentRepository = commentRepository;
this.postRepository = postRepository;
}
@Transactional
public Comment findCommentById(Long id) {
final Optional<Comment> comment = commentRepository.findById(id);
return comment.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public List<Comment> findAllComments() {
return commentRepository.findAll();
}
@Transactional
public Comment addComment(String text, Long userId, Long postId) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Comment data is null or empty");
}
try{
User user = userRepository.findById(userId).get();
Post post = postRepository.findById(postId).get();
Comment comment = new Comment(text, user, post);
user.addComment(comment);
post.addComment(comment);
userRepository.save(user);
postRepository.save(post);
return commentRepository.save(comment);
}
catch (Exception exception) {
return null;
}
}
@Transactional
public Comment updateComment(Long id, String text) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Comment data is null or empty");
}
Comment currentComment = findCommentById(id);
currentComment.setText(text);
final User owner = currentComment.getOwner();
userRepository.save(owner);
final Post post = currentComment.getPost();
postRepository.save(post);
return commentRepository.save(currentComment);
}
@Transactional
public Comment deleteComment(Long id) {
final Comment currentComment = findCommentById(id);
commentRepository.delete(currentComment);
return currentComment;
}
}

View File

@ -1,86 +0,0 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.CommentRepository;
import com.webproglabs.lab1.dao.PostRepository;
import com.webproglabs.lab1.dao.TopicRepository;
import com.webproglabs.lab1.dao.UserRepository;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import com.webproglabs.lab1.models.User;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class PostService {
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final UserRepository userRepository;
private final TopicRepository topicRepository;
public PostService(PostRepository postRepository, CommentRepository commentRepository, UserRepository userRepository, TopicRepository topicRepository) {
this.postRepository = postRepository;
this.commentRepository = commentRepository;
this.userRepository = userRepository;
this.topicRepository = topicRepository;
}
@Transactional
public Post findPostById(Long id) {
final Optional<Post> post = postRepository.findById(id);
return post.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public List<Post> findAllPosts() {
return postRepository.findAll();
}
@Transactional
public List<Post> findPostsInTopicByText(String text, Long topicId) {
Topic topic = topicRepository.findById(topicId).get();
return postRepository.findPostsByTextInTopic(text, topic);
}
@Transactional
public Post addPost (String text, Long authorId, Long topicId) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Post data is null or empty");
}
User author = userRepository.findById(authorId).get();
Topic topic = topicRepository.findById(topicId).get();
Post post = new Post(text, author, topic);
author.addPost(post);
topic.addPost(post);
userRepository.save(author);
topicRepository.save(topic);
return postRepository.save(post);
}
@Transactional
public Post updatePost(Long id, String text) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Post data is null or empty");
}
final Post currentPost = findPostById(id);
currentPost.setText(text);
final User author = currentPost.getAuthor();
userRepository.save(author);
final Topic topic = currentPost.getTopic();
topicRepository.save(topic);
return postRepository.save(currentPost);
}
@Transactional
public Post deletePost(Long id) {
final Post currentPost = findPostById(id);
postRepository.delete(currentPost);
return currentPost;
}
}

View File

@ -1,62 +0,0 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.TopicRepository;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.List;
@Service
public class TopicService {
private final TopicRepository topicRepository;
public TopicService(TopicRepository topicRepository) {
this.topicRepository = topicRepository;
}
@Transactional
public Topic addTopic(String name, String description) {
if (!StringUtils.hasText(name) || !StringUtils.hasText(description)) {
throw new IllegalArgumentException("Name is null or empty");
}
final Topic topic = new Topic(name, description);
return topicRepository.save(topic);
}
@Transactional
public Topic findTopicById(Long id) {
final Topic topic = topicRepository.findById(id).orElse(null);
if (topic == null) {
throw new EntityNotFoundException(String.format(" topic with id [%s] is not found", id));
}
return topic;
}
@Transactional
public List<Topic> findAllTopics() {
return topicRepository.findAll();
}
@Transactional
public Topic updateTopic(Long id, String name, String description) {
if (!StringUtils.hasText(name) || !StringUtils.hasText(description)) {
throw new IllegalArgumentException("Name is null or empty");
}
final Topic topic = findTopicById(id);
topic.setName(name);
topic.setDescription(description);
return topicRepository.save(topic);
}
@Transactional
public Topic deleteTopic(Long id) {
final Topic topic = findTopicById(id);
topicRepository.delete(topic);
return topic;
}
}

View File

@ -1,102 +0,0 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.UserRepository;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.models.UserRole;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Transactional
public User findUserById(Long id) {
final Optional<User> user = userRepository.findById(id);
return user.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public User findUserByLogin(String login) {
final User user = userRepository.findOneByLoginIgnoreCase(login).orElse(null);
return user;
}
@Transactional
public List<User> findAllUsers() {
return userRepository.findAll();
}
@Transactional
public User createUser(String login, String password, String passwordConfirm, UserRole role) {
if (findUserByLogin(login) != null) {
throw new IllegalArgumentException("User " + login + " already exists");
}
final User user = new User(login, passwordEncoder.encode(password), role);
if (!Objects.equals(password, passwordConfirm)) {
throw new IllegalArgumentException("Passwords not equals");
}
return userRepository.save(user);
}
@Transactional
public User createUser(String login, String password, String passwordConfirm) {
return createUser(login, password, passwordConfirm, UserRole.USER);
}
@Transactional
public User updateUser(Long id, String login, String password, String passwordConfirm) {
if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) {
throw new IllegalArgumentException("User data is null or empty");
}
final User currentUser = findUserById(id);
if (Objects.equals(password, currentUser.getPassword())) {
throw new IllegalArgumentException("New password is the same as old");
}
if (!Objects.equals(password, passwordConfirm)) {
throw new IllegalArgumentException("Password mismatch");
}
currentUser.setLogin(login);
currentUser.setPassword(passwordEncoder.encode(password));
return userRepository.save(currentUser);
}
@Transactional
public User deleteUser(Long id) {
final User currentUser = findUserById(id);
userRepository.delete(currentUser);
return currentUser;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final User userEntity = findUserByLogin(username);
if (userEntity == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
}
}

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{feedPosts}" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div layout:fragment="modalFeed">
<div class="modal fade" id="commentCreate" tabindex="-1" role="dialog" aria-labelledby="commentCreateLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="commentCreateLabel">Создать комментарий</h5>
</div>
<form th:action="@{/feed/comment/{id}/{topicId}/{postId}/{text} (id=${selectedProfile.id}, topicId=${selectedTopic.id}, postId=${selectedPost.id}, text=${commentInputField}) }" method="post" class="modal-body text-center">
<p>Текст комментария:</p>
<input th:value="${commentInputField}" id="commentInputField" name="commentInputField" type="text" class="mb-2">
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Сохранить</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="ru"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8"/>
<title>СоцСеточка</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="icon" href="/favicon.svg">
<script type="text/javascript" src="/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/webjars/bootstrap/5.1.3/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/webjars/font-awesome/6.1.0/css/all.min.css"/>
<link rel="stylesheet" href="/css/style.css"/>
</head>
<body>
<div class="bg-light w-75 rounded-pill text-center pt-4 pb-4 mt-3 mx-auto">
<p class='text-center h3'> СоцСеточка</p>
</div>
<div>
<p class='h4 mt-2 text-center'>
<a sec:authorize="hasRole('ROLE_ADMIN')" href="/topic" class="text-decoration-none m-3 text-secondary ">Темы</a>
<a sec:authorize="hasRole('ROLE_ADMIN')" href="/users" class="text-decoration-none m-3 text-secondary ">Пользователи</a>
<a sec:authorize="isAuthenticated()" href="/feed" class="text-decoration-none m-3 text-secondary ">Лента</a>
<a sec:authorize="isAuthenticated()" href="/users/settings" class="text-decoration-none m-3 text-secondary ">Настройки</a>
<a sec:authorize="isAuthenticated()" href="/logout" class="text-decoration-none m-3 text-secondary ">
Выход
</a>
</p>
</div>
<div >
<div layout:fragment="content"></div>
</div>
</body>
<script type="text/javascript">
window.onload = () => {
$('#postEdit').modal('show');
$('#commentCreate').modal('show');
$('#commentEdit').modal('show');
}
</script>
</html>

View File

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{feedPosts}" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div layout:fragment="modalFeed">
<div class="modal fade" id="commentEdit" tabindex="-1" role="dialog" aria-labelledby="commentEditLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="commentEditLabel">Редактировать комментарий</h5>
</div>
<form th:action="@{/feed/editComment/{id}/{commentId}/{text} (id=${selectedTopic.id}, commentId=${selectedComment.id}, text=${commentEditField}) }" method="post" class="modal-body text-center">
<p>Новый текст комментария:</p>
<input th:attr="value = ${selectedComment.text}" id="commentEditField" name="commentEditField" type="text" class="mb-2">
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Изменить</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{feedPosts}" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div layout:fragment="modalFeed">
<div class="modal fade" id="postEdit" tabindex="-1" role="dialog" aria-labelledby="postEditLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="postEditLabel">Редактировать пост</h5>
</div>
<form th:action="@{/feed/editPost/{id}/{topicId}/{text} (id=${selectedPost.id}, topicId=${selectedTopic.id}, text=${postEditField} ) }" method="post" class="modal-body text-center">
<p>Текст поста:</p>
<input th:attr="value = ${selectedPost.text}" id="postEditField" name="postEditField" type="text" class="mb-2">
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Сохранить</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div class="container" layout:fragment="content">
<div class="alert alert-danger">
<span th:text="${error}"></span>
</div>
<a href="/">На главную</a>
</div>
</body>
</html>

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div class="text-center">
<div class="dropdown text-center mx-auto w-75">
<div class="text-center mt-3">
<button class="btn btn-secondary dropdown-toggle mb-3 w-100" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Выбор темы
</button>
<ul class="dropdown-menu w-100" >
<li th:each="topic: ${topics}">
<a class="dropdown-item" th:href="@{/feed/{id}(id=${topic.id})}" th:text="${topic.name + '. ' + topic.description}">
</a>
</li>
</ul>
</div>
</div>
<div layout:fragment="contentFeed"></div>
</div>
</div>
</body>
</html>

View File

@ -1,110 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{feed}" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div layout:fragment="contentFeed">
<div class='mb-3 mx-auto w-75'>
<div class="text-center w-100 border border-2 bg-light">
<p class="text-center h3" th:text="${selectedTopic.name}"></p>
<p class="text-center fw-light" th:text="${selectedTopic.description}"></p>
</div>
<p class="text-end h3 " th:text="${selectedProfile.login}"></p>
</div>
<div class='h3 m-3 d-flex justify-content-between text-center mx-auto w-75'>
Лента
<button type='button' class="btn btn-primary ms-5 mb-3 " data-bs-toggle="modal" data-bs-target="#postCreate">
Добавить новый пост
</button>
</div>
<form th:action="@{/feed/filter/{id}/{text} (id=${selectedTopic.id}, text=${searchField}) }" method="get">
<input th:value="${searchField}" id="searchField" name="searchField" type="text" class="mb-2" style="width: 70%" placeholder="Поиск...">
<button type='submit' class="btn btn-primary mb-2" style="width: 5%">
Найти
</button>
</form>
<div th:each="post: ${posts}" class="text-center mx-auto w-75 mb-3 ">
<div class="border p-2">
<p th:text="${post.text}" class="h4 text-start"></p>
<div class="d-flex justify-content-between fst-italic">
<div>
Автор:
<a th:text="${post.getAuthor()}" class="text-start fst-italic" th:href="@{/users/{login}(login=${post.getAuthor()})}"></a>
</div>
<div th:if="${selectedProfile.getLogin() == post.getAuthor()}" class="d-flex justify-content-between fst-italic">
<form th:action="@{/feed/deletePost/{id}/{topicId} (id=${post.id}, topicId=${selectedTopic.id})}" method="post" >
<button type="submit" class="btn btn-danger me-1 mb-1 ms-2" >
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</form>
<form th:action="@{/feed/postModal/{id}/{topicId} (id=${post.id}, topicId=${selectedTopic.id})}" method="get">
<button type="submit" class="btn btn-warning mb-1" data-bs-toggle="modal" data-bs-target="#postEdit" >
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
<div class="border p-2" th:if="${post.comments.size() > 0}" >
<div class="text-start">
<p class="text-start h5">Комментарии:</p>
<div th:each="comment: ${post.comments}" >
<p class="fst-italic" th:text="${comment.getAuthor() + ':'}"> </p>
<div class="d-flex justify-content-between fst-italic">
<p class="ms-3" th:text="${comment.text}"></p>
<div th:if="${selectedProfile.getLogin() == comment.getAuthor()}" class="d-flex justify-content-between fst-italic">
<form th:action="@{/feed/deleteComment/{id}/{topicId} (id=${comment.id}, topicId=${selectedTopic.id})}" method="post" >
<button type="submit" class="btn btn-danger me-1 mb-1"> <i class="fa fa-trash" aria-hidden="true"> </i> </button>
</form>
<form th:action="@{/feed/commentEditModal/{id}/{topicId} (id=${comment.id}, topicId=${selectedTopic.id})}" method="get">
<button type="submit" class="btn btn-warning mb-1" data-bs-toggle="modal" data-bs-target="#postEdit" >
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<form th:action="@{/feed/commentModal/{topicId}/{postId}/ ( topicId=${selectedTopic.id}, postId=${post.id} ) }" method="get" class="text-end">
<button type="submit" class="btn btn-info mb-3" data-bs-toggle="modal" data-bs-target="#commentCreate">Добавить комментарий</button>
</form>
</div>
<div class="modal fade" id="postCreate" tabindex="-1" role="dialog" aria-labelledby="postCreateLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="postCreateLabel">Создать пост</h5>
</div>
<form th:action="@{/feed/{topicId}/post/{id}/{text} (topicId=${selectedTopic.id}, id=${selectedProfile.id}, text=${postInputField}) }" method="post" class="modal-body text-center">
<p>Текст поста:</p>
<input th:value="${postInputField}" id="postInputField" name="postInputField" type="text" class="mb-2">
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Сохранить</button>
</form>
</div>
</div>
</div>
<div layout:fragment="modalFeed"></div>
</div>
</body>
</html>

View File

@ -1,31 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div class="container" layout:fragment="content">
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
Пользователь не найден или пароль указан не верно
</div>
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
Выход успешно произведен
</div>
<div th:if="${param.created}" class="alert alert-success margin-bottom">
Пользователь '<span th:text="${param.created}"></span>' успешно создан
</div>
<form th:action="@{/login}" method="post" class="container-padding">
<div class="mb-3">
<input type="text" name="username" id="username" class="form-control"
placeholder="Логин" required="true" autofocus="true"/>
</div>
<div class="mb-3">
<input type="password" name="password" id="password" class="form-control"
placeholder="Пароль" required="true"/>
</div>
<button type="submit" class="btn btn-success button-fixed">Войти</button>
<a class="btn btn-primary button-fixed" href="/signup">Регистрация</a>
</form>
</div>
</body>
</html>

View File

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div class="container container-padding" layout:fragment="content">
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<form action="#" th:action="@{/signup}" th:object="${userDto}" method="post">
<div class="mb-3">
<input type="text" class="form-control" th:field="${userDto.login}"
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.password}"
placeholder="Пароль" required="true" minlength="4" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.passwordConfirm}"
placeholder="Пароль (подтверждение)" required="true" minlength="4" maxlength="64"/>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">Создать</button>
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"
xmlns:th="http://www.w3.org/1999/xhtml">
<body>
<div class="container container-padding" layout:fragment="content">
<div class="h4 text-center">Изменить топик</div>
<form action="#" th:action="@{/topic/edit/{id} (id=${topic.id})}" th:object="${topicDto}" method="post">
<p>Новое название:</p>
<input th:field="${topicDto.name}" type="text" class="mb-2 form-control" required="true" />
<p>Новое описание:</p>
<input th:field="${topicDto.description}" type="text" class="mb-2 form-control" required="true" />
<button type="submit" class="btn btn-success" >Сохранить изменения</button>
</form>
</div>
</body>
</html>

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div class="container container-padding" layout:fragment="content">
<form action="#" th:action="@{/topic/create}" th:object="${topicDto}" method="post">
<p>Название:</p>
<input th:field="${topicDto.name}" type="text" class="mb-2 form-control" required="true" />
<p>Описание:</p>
<input th:field="${topicDto.description}" type="text" class="mb-2 form-control" required="true" />
<button type="submit" class="btn btn-success">Добавить</button>
</form>
<div th:each="topic: ${topics}" class="border p-3 m-3 shadow-lg bg-body rounded">
<div th:text="${'Топик: ' + topic.name}"></div>
<div th:text="${'Описание: ' + topic.description}"></div>
<form action="#" th:action="@{/topic/edit/{id} (id=${topic.id})}" method="get" class="text-end m-1">
<button type="submit" class="btn btn-warning">Изменить</button>
</form>
<form action="#" th:action="@{/topic/delete/{id} (id=${topic.id})}" method="post" class="text-end m-1">
<button type="submit" class="btn btn-danger" >Удалить</button>
</form>
</div>
</div>
</body>
</html>

View File

@ -1,31 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div class="text-center">
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<div th:if="${success}" th:text="${success}" class="margin-bottom text-success"></div>
<div class="text-start mx-auto w-50 border p-5">
<p class='h5'>Изменение данных пользователя</p>
<p th:text="${'Логин: ' + user.login}"></p>
<form action="#" th:action="@{/users/edit/{id} (id=${user.id})}" th:object="${userDto}" method="post">
<p>Новый логин:</p>
<input th:field="${userDto.login}" type="text" class="mb-2 form-control" required="true" autofocus="true" maxlength="64"/>
<p>Новый пароль:</p>
<input th:field="${userDto.password}" type="password" class="mb-2 form-control" required="true" minlength="4" maxlength="64"/>
<p>Повторите пароль:</p>
<input th:field="${userDto.passwordConfirm}" type="password" class="mb-2 form-control" required="true" minlength="4" maxlength="64"/>
<button type="submit" class="btn btn-success" >Сохранить изменения</button>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div class="text-center">
<div class="text-start mx-auto w-50 border p-5">
<p class='h5'>Профиль</p>
<p th:text="${'Логин: ' + user.login}"></p>
<p class='h6 '>Список постов пользователя: </p>
<div th:if="${user.posts.size()>0}">
<div th:each="post: ${user.posts}">
<p th:text="${post.text}" class="mb-3 ms-2 border p-2"></p>
</div>
</div>
<p th:unless="${user.posts.size()>0}">
Нет постов
</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div layout:fragment="content" class="text-center">
<div th:each="profile: ${profiles}" class="mb-2">
<div class="text-center">
<div class="text-start mx-auto w-50 border shadow p-3">
<p class='h5'>Профиль</p>
<p th:text="${'Логин: ' + profile.login}"></p>
<p class='h6 '>Список постов пользователя: </p>
<div th:if="${profile.posts.size()>0}">
<div th:each="post: ${profile.posts}">
<p th:text="${post.text}" class="mb-3 ms-2 border p-2"></p>
</div>
</div>
<p th:unless="${profile.posts.size()>0}">
Нет постов
</p>
<div class="text-end" th:if="${profile.login!='admin'}">
<form action="#" th:action="@{/users/{id} (id=${profile.id})}" method="post" >
<button type="submit" class="btn btn-danger">Удалить</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>