Half Lab5
This commit is contained in:
parent
78a86fb68e
commit
dd87a58873
14
build.gradle
14
build.gradle
@ -14,11 +14,23 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||
|
||||
implementation 'org.webjars:bootstrap:5.1.3'
|
||||
implementation 'org.webjars:jquery:3.6.0'
|
||||
implementation 'org.webjars:font-awesome:6.1.0'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.1.210'
|
||||
|
||||
implementation 'org.hibernate.validator:hibernate-validator'
|
||||
|
||||
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
BIN
data.mv.db
BIN
data.mv.db
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit 62649c78e6740e7f779a68f9588c91a318d4c0b5
|
||||
Subproject commit 2bc9e21c22b7fbd6679612d7e6fc1170964cc128
|
@ -2,10 +2,17 @@ package com.webproglabs.lab1;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
public static final String REST_API = "/api";
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
WebMvcConfigurer.super.addViewControllers(registry);
|
||||
registry.addViewController("rest-test");
|
||||
}
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.webproglabs.lab1.lab34.controller;
|
||||
|
||||
import com.webproglabs.lab1.WebConfiguration;
|
||||
import com.webproglabs.lab1.lab34.model.Comment;
|
||||
import com.webproglabs.lab1.lab34.model.Profile;
|
||||
import com.webproglabs.lab1.lab34.services.CommentService;
|
||||
@ -8,7 +9,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/comment")
|
||||
@RequestMapping(WebConfiguration.REST_API + "/comment")
|
||||
public class CommentController {
|
||||
private final CommentService commentService;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.webproglabs.lab1.lab34.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.webproglabs.lab1.lab34.model.Comment;
|
||||
|
||||
public class CommentDto {
|
||||
@ -11,6 +12,9 @@ public class CommentDto {
|
||||
this.text = comment.getText();
|
||||
}
|
||||
|
||||
public Long getId() {return id;}
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
public String getText() {return text;}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.webproglabs.lab1.lab34.controller;
|
||||
|
||||
import com.webproglabs.lab1.WebConfiguration;
|
||||
import com.webproglabs.lab1.lab34.services.PostService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -7,7 +8,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/post")
|
||||
@RequestMapping(WebConfiguration.REST_API + "/post")
|
||||
public class PostController {
|
||||
private final PostService postService;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.webproglabs.lab1.lab34.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.webproglabs.lab1.lab34.model.Comment;
|
||||
import com.webproglabs.lab1.lab34.model.Post;
|
||||
import com.webproglabs.lab1.lab34.model.Profile;
|
||||
@ -12,15 +13,22 @@ public class PostDto {
|
||||
private String text;
|
||||
private List<CommentDto> comments = new ArrayList<>();
|
||||
|
||||
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 Long getId() {return id;}
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
public String getText() {return text;}
|
||||
public List<CommentDto> getComments() {return comments;}
|
||||
public String getAuthor() {return authorLogin;}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.webproglabs.lab1.lab34.controller;
|
||||
|
||||
import com.webproglabs.lab1.WebConfiguration;
|
||||
import com.webproglabs.lab1.lab34.services.ProfileService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -7,7 +8,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/profile")
|
||||
@RequestMapping(WebConfiguration.REST_API + "/profile")
|
||||
public class ProfileController {
|
||||
private final ProfileService profileService;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.webproglabs.lab1.lab34.controller;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.webproglabs.lab1.lab34.model.Comment;
|
||||
import com.webproglabs.lab1.lab34.model.Post;
|
||||
import com.webproglabs.lab1.lab34.model.Profile;
|
||||
@ -26,7 +27,10 @@ public class ProfileDto {
|
||||
}
|
||||
}
|
||||
|
||||
public Long getId() {return id;}
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
public String getLogin() {return login;}
|
||||
public String getPassword() {return password;}
|
||||
public List<CommentDto> getComments() {return comments;}
|
||||
|
@ -0,0 +1,75 @@
|
||||
package com.webproglabs.lab1.lab34.controller.mvc_controllers;
|
||||
|
||||
import com.webproglabs.lab1.lab34.controller.PostDto;
|
||||
import com.webproglabs.lab1.lab34.controller.ProfileDto;
|
||||
import com.webproglabs.lab1.lab34.model.Post;
|
||||
import com.webproglabs.lab1.lab34.services.PostService;
|
||||
import com.webproglabs.lab1.lab34.services.ProfileService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/feed")
|
||||
public class FeedMvcController {
|
||||
|
||||
private final ProfileService profileService;
|
||||
private final PostService postService;
|
||||
|
||||
public FeedMvcController(ProfileService profileService, PostService postService) {
|
||||
this.profileService = profileService;
|
||||
this.postService = postService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getFeedPage(Model model) {
|
||||
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
|
||||
return "feed";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/{id}"})
|
||||
public String getFeedPageAuthorized(@PathVariable(required = false) Long id, Model model) {
|
||||
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
|
||||
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
|
||||
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(id)));
|
||||
|
||||
return "feedPosts";
|
||||
}
|
||||
|
||||
@GetMapping(value= {"/filter/{id}/"})
|
||||
public String getFeedPageFiltered(@PathVariable(required = false) Long id, @RequestParam(value="searchField") String searchField, Model model) {
|
||||
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
|
||||
model.addAttribute("posts", postService.findFilteredPosts(searchField).stream().map(PostDto::new).toList());
|
||||
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(id)));
|
||||
return "feedPosts";
|
||||
}
|
||||
|
||||
@PostMapping(value={"/post/{id}/"})
|
||||
public String createPost(@PathVariable(required = false) Long id, @RequestParam(value="postInputField") String postInputField) {
|
||||
postService.addPost(postInputField, new ArrayList<>(), id);
|
||||
return "redirect:/feed/" + id.toString();
|
||||
}
|
||||
|
||||
@PostMapping(value = {"/deletePost/{id}/{authorId}"})
|
||||
public String deletePost(@PathVariable(required = false) Long id, @PathVariable(required = false) Long authorId) {
|
||||
postService.deletePost(id);
|
||||
return "redirect:/feed/" + authorId.toString();
|
||||
}
|
||||
|
||||
@GetMapping(value = {"postModal/{id}/{authorId}"})
|
||||
public String getPostEditModal(@PathVariable(required = false) Long id,@PathVariable(required = false) Long authorId, Model model) {
|
||||
model.addAttribute("selectedPost", new PostDto(postService.findPost(id)));
|
||||
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
|
||||
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
|
||||
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(authorId)));
|
||||
return "editPostModal";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"editPost/{id}/{authorId}/"})
|
||||
public String editPost(@PathVariable(required = false) Long id, @PathVariable(required = false) Long authorId, @RequestParam(value="postEditField") String postEditField) {
|
||||
postService.updatePost(id, postEditField);
|
||||
return "redirect:/feed/" + authorId.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.webproglabs.lab1.lab34.controller.mvc_controllers;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/")
|
||||
public class ProfileMvcController {
|
||||
|
||||
@GetMapping
|
||||
public String index() {
|
||||
return "default";
|
||||
}
|
||||
}
|
@ -53,7 +53,7 @@ public class PostService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Post addPost(String text, List<Comment> comments, Long authorId) {
|
||||
public Post addPost (String text, List<Comment> comments, Long authorId) {
|
||||
if (!StringUtils.hasText(text)) {
|
||||
throw new IllegalArgumentException("Post data is null or empty");
|
||||
}
|
||||
|
15
src/main/resources/public/css/style.css
Normal file
15
src/main/resources/public/css/style.css
Normal file
@ -0,0 +1,15 @@
|
||||
.container-padding {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.margin-bottom {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.button-fixed {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.button-sm {
|
||||
padding: 1px;
|
||||
}
|
4
src/main/resources/public/favicon.svg
Normal file
4
src/main/resources/public/favicon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. -->
|
||||
<path d="M448 48V384c-63.09 22.54-82.34 32-119.5 32c-62.82 0-86.6-32-149.3-32C158.6 384 142.6 387.6 128 392.2v-64C142.6 323.6 158.6 320 179.2 320c62.73 0 86.51 32 149.3 32C348.9 352 364.1 349 384 342.7v-208C364.1 141 348.9 144 328.5 144c-62.82 0-86.6-32-149.3-32C128.4 112 104.3 132.6 64 140.7v307.3C64 465.7 49.67 480 32 480S0 465.7 0 448V63.1C0 46.33 14.33 32 31.1 32S64 46.33 64 63.1V76.66C104.3 68.63 128.4 48 179.2 48c62.73 0 86.51 32 149.3 32C365.7 80 384.9 70.54 448 48z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 727 B |
38
src/main/resources/templates/default.html
Normal file
38
src/main/resources/templates/default.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Лабораторная работа 5</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>
|
||||
<p class='text-center m-3 h3'> Лабораторная работа 5</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class='h4 text-center'>
|
||||
<a href="/" class="text-decoration-none m-3">Профили</a>
|
||||
<a href="/feed" class="text-decoration-none m-3">Лента</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div >
|
||||
<div layout:fragment="content"></div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
window.onload = () => {
|
||||
$('#postEdit').modal('show');
|
||||
}
|
||||
</script>
|
||||
</html>
|
37
src/main/resources/templates/editPostModal.html
Normal file
37
src/main/resources/templates/editPostModal.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!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}/{authorId}/{text} (id=${selectedPost.id}, authorId=${selectedProfile.id}, text=${postEditField} ) }" method="post" class="modal-body text-center">
|
||||
<p>Текст поста:</p>
|
||||
<input th:value="${postEditField}" 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>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = () => {
|
||||
$('#postEdit').modal('show');
|
||||
}
|
||||
</script>
|
||||
</html>
|
36
src/main/resources/templates/feed.html
Normal file
36
src/main/resources/templates/feed.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!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-25 ">
|
||||
<div class="text-end mt-3">
|
||||
<button class="btn btn-secondary dropdown-toggle ms-3 mb-3 " type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Выбор пользователя
|
||||
</button>
|
||||
<ul class="dropdown-menu " >
|
||||
<li th:each="profile: ${profiles}">
|
||||
<a class="dropdown-item" th:href="@{/feed/{id}(id=${profile.id})}" th:text="${profile.login}">
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div layout:fragment="contentFeed"></div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
window.onload = () => {
|
||||
$('#postEdit').modal('show');
|
||||
}
|
||||
</script>
|
||||
|
||||
</html>
|
81
src/main/resources/templates/feedPosts.html
Normal file
81
src/main/resources/templates/feedPosts.html
Normal file
@ -0,0 +1,81 @@
|
||||
<!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='h3 mb-3 mx-auto w-25'>
|
||||
<p class="text-end" th:text="${selectedProfile.login}"></p>
|
||||
</div>
|
||||
<div class='h3 m-3 d-flex justify-content-between text-center mx-auto w-25'>
|
||||
|
||||
Лента
|
||||
<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=${selectedProfile.id}, text=${searchField}) }" method="get">
|
||||
<input th:value="${searchField}" id="searchField" name="searchField" type="text" class="mb-2" style="width: 20%" 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-25 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" ></a>
|
||||
</div>
|
||||
<div th:if="${selectedProfile.getLogin() == post.getAuthor()}" class="d-flex justify-content-between fst-italic">
|
||||
<form th:action="@{/feed/deletePost/{id}/{authorId} (id=${post.id}, authorId=${selectedProfile.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}/{authorId} (id=${post.id}, authorId=${selectedProfile.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 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/post/{id}/{text} (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>
|
||||
<script type="text/javascript">
|
||||
window.onload = () => {
|
||||
$('#postEdit').modal('show');
|
||||
}
|
||||
</script>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user