#7 -- add mvc for meetings
This commit is contained in:
parent
8eef40a885
commit
27ff4a3f4e
BIN
data/db.mv.db
BIN
data/db.mv.db
Binary file not shown.
@ -45,7 +45,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
|||||||
log.debug("Security enabled");
|
log.debug("Security enabled");
|
||||||
http.authorizeRequests()
|
http.authorizeRequests()
|
||||||
.antMatchers("/").permitAll()
|
.antMatchers("/").permitAll()
|
||||||
.antMatchers("/login", "/index", "/news/**", "/h2-console/*", "/h2-console").permitAll()
|
.antMatchers("/login", "/index", "/news/**", "/meetings/**", "/h2-console/*", "/h2-console").permitAll()
|
||||||
.antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
.antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
.and()
|
.and()
|
||||||
|
@ -59,6 +59,7 @@ public class NewsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/editNews/{newsId}")
|
@GetMapping("/editNews/{newsId}")
|
||||||
|
@Secured({UserRoleConstants.ADMIN})
|
||||||
public String editNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
public String editNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
||||||
model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News());
|
model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News());
|
||||||
return "editNews";
|
return "editNews";
|
||||||
@ -82,6 +83,7 @@ public class NewsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("deleteNews/{newsId}")
|
@GetMapping("deleteNews/{newsId}")
|
||||||
|
@Secured({UserRoleConstants.ADMIN})
|
||||||
public String delete(@PathVariable(value = "newsId") Integer id) {
|
public String delete(@PathVariable(value = "newsId") Integer id) {
|
||||||
newsService.delete(id);
|
newsService.delete(id);
|
||||||
return "redirect:/news/news";
|
return "redirect:/news/news";
|
||||||
|
55
src/main/java/ru/ulstu/meeting/Meeting.java
Normal file
55
src/main/java/ru/ulstu/meeting/Meeting.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package ru.ulstu.meeting;
|
||||||
|
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import ru.ulstu.model.BaseEntity;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Lob;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Meeting extends BaseEntity {
|
||||||
|
@NotEmpty(message = "Заголовок не может быть пустым")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
|
||||||
|
private Date date;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@NotEmpty(message = "Текст заседания не может быть пустым")
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public Meeting() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Meeting(String title, String text, Date date) {
|
||||||
|
this.title = title;
|
||||||
|
this.date = date;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDate(Date date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
88
src/main/java/ru/ulstu/meeting/MeetingController.java
Normal file
88
src/main/java/ru/ulstu/meeting/MeetingController.java
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.ulstu.meeting;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.security.access.annotation.Secured;
|
||||||
|
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.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import ru.ulstu.model.OffsetablePageRequest;
|
||||||
|
import ru.ulstu.model.UserRoleConstants;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("meetings")
|
||||||
|
public class MeetingController {
|
||||||
|
private final static int DEFAULT_PAGE_SIZE = 10;
|
||||||
|
private final MeetingService meetingService;
|
||||||
|
|
||||||
|
public MeetingController(MeetingService meetingService) {
|
||||||
|
this.meetingService = meetingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/meetings")
|
||||||
|
public String listMeetings(Model model,
|
||||||
|
@RequestParam Optional<Integer> page,
|
||||||
|
@RequestParam Optional<Integer> size) {
|
||||||
|
int currentPage = page.orElse(1);
|
||||||
|
int pageSize = size.orElse(DEFAULT_PAGE_SIZE);
|
||||||
|
|
||||||
|
Page<Meeting> meetingsPage = meetingService.getMeetings(new OffsetablePageRequest(currentPage - 1, pageSize));
|
||||||
|
model.addAttribute("meetings", meetingsPage);
|
||||||
|
int totalPages = meetingsPage.getTotalPages();
|
||||||
|
if (totalPages > 0) {
|
||||||
|
List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
||||||
|
.boxed()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
model.addAttribute("pageNumbers", pageNumbers);
|
||||||
|
}
|
||||||
|
return "meetings";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/editMeeting/{meetingId}")
|
||||||
|
@Secured({UserRoleConstants.ADMIN})
|
||||||
|
public String editMeeting(@PathVariable(value = "meetingId") Integer id, Model model) {
|
||||||
|
model.addAttribute("meeting", (id != null && id != 0) ? meetingService.getById(id) : new Meeting());
|
||||||
|
return "editMeeting";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/meetings/{meetingId}")
|
||||||
|
public String viewMeeting(@PathVariable(value = "meetingId") Integer id, Model model) {
|
||||||
|
model.addAttribute("meeting", id != null ? meetingService.getById(id) : new Meeting());
|
||||||
|
return "viewMeeting";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("saveMeeting")
|
||||||
|
@Secured({UserRoleConstants.ADMIN})
|
||||||
|
public String saveNews(@Valid @ModelAttribute Meeting meeting,
|
||||||
|
BindingResult result) {
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
return "editMeeting";
|
||||||
|
}
|
||||||
|
meetingService.save(meeting);
|
||||||
|
return "redirect:/meetings/meetings";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("deleteMeeting/{meetingId}")
|
||||||
|
@Secured({UserRoleConstants.ADMIN})
|
||||||
|
public String delete(@PathVariable(value = "meetingId") Integer id) {
|
||||||
|
meetingService.delete(id);
|
||||||
|
return "redirect:/meetings/meetings";
|
||||||
|
}
|
||||||
|
}
|
9
src/main/java/ru/ulstu/meeting/MeetingRepository.java
Normal file
9
src/main/java/ru/ulstu/meeting/MeetingRepository.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package ru.ulstu.meeting;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
interface MeetingRepository extends JpaRepository<Meeting, Integer> {
|
||||||
|
Page<Meeting> findByOrderByDateDesc(Pageable pageable);
|
||||||
|
}
|
47
src/main/java/ru/ulstu/meeting/MeetingService.java
Normal file
47
src/main/java/ru/ulstu/meeting/MeetingService.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package ru.ulstu.meeting;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.service.NewsService;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MeetingService {
|
||||||
|
private final MeetingRepository meetingRepository;
|
||||||
|
private final NewsService newsService;
|
||||||
|
|
||||||
|
public MeetingService(MeetingRepository meetingRepository,
|
||||||
|
NewsService newsService) {
|
||||||
|
this.meetingRepository = meetingRepository;
|
||||||
|
this.newsService = newsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void create(Meeting meeting) {
|
||||||
|
meetingRepository.save(meeting);
|
||||||
|
newsService.create("Очередное заседание семинара", "Заседание семинара состоится " + meeting.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Meeting meeting) {
|
||||||
|
if (meeting.getId() != null && (meeting.getId() != 0)) {
|
||||||
|
meetingRepository.save(meeting);
|
||||||
|
} else {
|
||||||
|
create(meeting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Meeting getById(@NotNull Integer id) {
|
||||||
|
return meetingRepository
|
||||||
|
.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Запись о заседании не найдена"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Integer id) {
|
||||||
|
meetingRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<Meeting> getMeetings(Pageable pageable) {
|
||||||
|
return meetingRepository.findByOrderByDateDesc(pageable);
|
||||||
|
}
|
||||||
|
}
|
@ -10,5 +10,8 @@
|
|||||||
<a href="/news/editNews/0" class="btn btn-outline-dark">
|
<a href="/news/editNews/0" class="btn btn-outline-dark">
|
||||||
<i class="fa fa-plus-square" aria-hidden="true">Добавить новость</i>
|
<i class="fa fa-plus-square" aria-hidden="true">Добавить новость</i>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/meetings/editMeeting/0" class="btn btn-outline-dark">
|
||||||
|
<i class="fa fa-plus-square" aria-hidden="true">Добавить заседание</i>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
@ -36,10 +36,10 @@
|
|||||||
<a class="nav-link" href="/news/news">Новости</a>
|
<a class="nav-link" href="/news/news">Новости</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/news">Заседания</a>
|
<a class="nav-link" href="/meetings/meetings">Заседания</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/news">Отчеты</a>
|
<a class="nav-link" href="/reports">Отчеты</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/admin">Администратору</a>
|
<a class="nav-link" href="/admin">Администратору</a>
|
||||||
|
33
src/main/resources/templates/editMeeting.html
Normal file
33
src/main/resources/templates/editMeeting.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
|
<html
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<h3>Редактирование заседания:</h3>
|
||||||
|
<form action="#" th:action="@{/meetings/saveMeeting}" th:object="${meeting}" method="post">
|
||||||
|
<input type="hidden" th:field="*{id}">
|
||||||
|
<input type="hidden" th:field="*{version}">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Тема заседания</label>
|
||||||
|
<input type="text" class="form-control" id="title" th:field="*{title}" placeholder="Тема заседания">
|
||||||
|
<p th:if="${#fields.hasErrors('title')}" th:class="${#fields.hasErrors('title')}? error">
|
||||||
|
Не может быть пустым</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="text">Текст о заседании</label>
|
||||||
|
<textarea class="form-control" id="text" th:field="*{text}" placeholder="Текст о заседании"
|
||||||
|
style="height: 300px"></textarea>
|
||||||
|
<p th:if="${#fields.hasErrors('text')}" th:class="${#fields.hasErrors('text')}? error">
|
||||||
|
Не может быть пустым</p>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-dark">Сохранить</button>
|
||||||
|
<a href="javascript:history.back()" class="btn btn-outline-dark">Отмена</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</html>
|
40
src/main/resources/templates/meetings.html
Normal file
40
src/main/resources/templates/meetings.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
|
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<div th:each="m : ${meetings}" class="news">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10">
|
||||||
|
<a th:href="@{'/meetings/meetings/' + ${m.id}}" class="link-dark"><h5 th:text="${m.title}"/></a>
|
||||||
|
</div>
|
||||||
|
<div sec:authorize="hasRole('ROLE_ADMIN')" class="col-md-2" style="text-align: right">
|
||||||
|
<a th:href="@{'/meetings/editMeeting/' + ${m.id}}" class="link-dark">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
<a th:href="@{'/meetings/deleteMeeting/' + ${m.id}}" class="link-dark"
|
||||||
|
onclick="return confirm('Удалить запись о заседании?')">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div th:if="${meetings.totalPages > 0}" class="pagination">
|
||||||
|
<span style="float: left; padding: 5px 5px;">Страницы:</span>
|
||||||
|
</div>
|
||||||
|
<div th:if="${meetings.totalPages > 0}" class="pagination"
|
||||||
|
th:each="pageNumber : ${pageNumbers}">
|
||||||
|
<a th:href="@{/news/news(size=${meetings.size}, page=${pageNumber})}"
|
||||||
|
th:text=${pageNumber}
|
||||||
|
th:class="${pageNumber == meetings.number+1} ? active"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</html>
|
23
src/main/resources/templates/viewMeeting.html
Normal file
23
src/main/resources/templates/viewMeeting.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
|
<html
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<img class="news-image" src="/img/logo.svg"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h5 th:text="${meeting.title}"/>
|
||||||
|
<div th:text="${meeting.text}" class="news-item"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="javascript:history.back()" class="btn btn-outline-dark" style="text-align: right">Назад</a>
|
||||||
|
</div>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user