Merge pull request '1-db' (#2) from 1-db into master
Reviewed-on: http://git.athene.tech/romanov73/seminar/pulls/2
This commit is contained in:
commit
f8ff29e989
15
build.gradle
15
build.gradle
@ -32,12 +32,19 @@ dependencies {
|
||||
versionSwagger = '2.5.0'
|
||||
}
|
||||
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: versionSLF4J
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation'
|
||||
implementation group: 'org.springframework.boot', name:'spring-boot-starter-data-jpa'
|
||||
//implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security'
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: versionSLF4J
|
||||
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect'
|
||||
implementation group: 'org.javassist', name: 'javassist', version: '3.25.0-GA'
|
||||
//implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5'
|
||||
implementation group: 'com.h2database', name:'h2'
|
||||
implementation group: 'javax.xml.bind', name:'jaxb-api'
|
||||
implementation group: 'org.javassist', name:'javassist'
|
||||
|
||||
implementation group: 'org.eclipse.jetty', name: 'jetty-servlet', version: versionJetty
|
||||
|
||||
implementation group: 'org.webjars', name: 'jquery', version: '3.6.0'
|
||||
|
BIN
data/db.mv.db
Normal file
BIN
data/db.mv.db
Normal file
Binary file not shown.
@ -20,7 +20,9 @@ import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
public class MvcConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/{articlename:\\w+}");
|
||||
registry.addViewController("/index");
|
||||
registry.addViewController("/admin");
|
||||
registry.addViewController("/editNews");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,20 +9,20 @@ package ru.ulstu.controller;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import ru.ulstu.model.News;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import ru.ulstu.service.NewsService;
|
||||
|
||||
@Controller
|
||||
public class IndexController {
|
||||
|
||||
private final NewsService newsService;
|
||||
|
||||
public IndexController(NewsService newsService) {
|
||||
this.newsService = newsService;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(Model model) {
|
||||
model.addAttribute("news", List.of(new News("Открытие семинара", new GregorianCalendar(2022, 4, 1).getTime(),
|
||||
"На кафере \"Информационные системы\" Ульяновского государственного технического университета состоится открытие постоянно действующего семинара \"Анализ данных и процессов\". Семинар планируется проводить ежемесячно."),
|
||||
new News("Открытие семинара", new GregorianCalendar(2022, 4, 1).getTime(),
|
||||
"На кафере \"Информационные системы\" Ульяновского государственного технического университета состоится открытие постоянно действующего семинара \"Анализ данных и процессов\". Семинар планируется проводить ежемесячно.")));
|
||||
model.addAttribute("news", newsService.getAll());
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
|
56
src/main/java/ru/ulstu/controller/NewsController.java
Normal file
56
src/main/java/ru/ulstu/controller/NewsController.java
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
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 ru.ulstu.model.News;
|
||||
import ru.ulstu.service.NewsService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Controller
|
||||
public class NewsController {
|
||||
private final NewsService newsService;
|
||||
|
||||
public NewsController(NewsService newsService) {
|
||||
this.newsService = newsService;
|
||||
}
|
||||
|
||||
@GetMapping("/editNews/{newsId}")
|
||||
public String editNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
||||
model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News());
|
||||
return "editNews";
|
||||
}
|
||||
|
||||
@GetMapping("/news/{newsId}")
|
||||
public String viewNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
||||
model.addAttribute("news", id != null ? newsService.getById(id) : new News());
|
||||
return "news";
|
||||
}
|
||||
|
||||
@PostMapping("saveNews")
|
||||
public String saveNews(@Valid @ModelAttribute News news, BindingResult result) {
|
||||
if (result.hasErrors()) {
|
||||
return "editNews";
|
||||
}
|
||||
newsService.save(news);
|
||||
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@GetMapping("deleteNews/{newsId}")
|
||||
public String delete(@PathVariable(value = "newsId") Integer id) {
|
||||
newsService.delete(id);
|
||||
return "redirect:/";
|
||||
}
|
||||
}
|
83
src/main/java/ru/ulstu/model/BaseEntity.java
Normal file
83
src/main/java/ru/ulstu/model/BaseEntity.java
Normal file
@ -0,0 +1,83 @@
|
||||
package ru.ulstu.model;
|
||||
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Version;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity implements Serializable, Comparable<BaseEntity> {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.TABLE)
|
||||
private Integer id;
|
||||
|
||||
@Version
|
||||
private Integer version;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!getClass().isAssignableFrom(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
BaseEntity other = (BaseEntity) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull BaseEntity o) {
|
||||
return id != null ? id.compareTo(o.getId()) : -1;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.id = null;
|
||||
this.version = null;
|
||||
}
|
||||
}
|
@ -1,12 +1,28 @@
|
||||
package ru.ulstu.model;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Lob;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.Date;
|
||||
|
||||
public class News {
|
||||
private final static int MAX_NEWS_TEXT_PREVIEW_LENGTH = 200;
|
||||
private final String title;
|
||||
private final Date date;
|
||||
private final String text;
|
||||
@Entity
|
||||
public class News extends BaseEntity {
|
||||
private final static int MAX_NEWS_TEXT_PREVIEW_LENGTH = 800;
|
||||
|
||||
@NotEmpty(message = "Заголовок не может быть пустым")
|
||||
private String title;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
|
||||
private Date date;
|
||||
|
||||
@Lob
|
||||
@NotEmpty(message = "Текст новости не может быть пустым")
|
||||
private String text;
|
||||
|
||||
public News() {
|
||||
}
|
||||
|
||||
public News(String title, Date date, String text) {
|
||||
this.title = title;
|
||||
@ -22,12 +38,24 @@ public class News {
|
||||
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;
|
||||
}
|
||||
|
||||
public String getPreview() {
|
||||
return text.length() > MAX_NEWS_TEXT_PREVIEW_LENGTH
|
||||
return text != null && text.length() > MAX_NEWS_TEXT_PREVIEW_LENGTH
|
||||
? text.substring(0, MAX_NEWS_TEXT_PREVIEW_LENGTH) + "..."
|
||||
: text;
|
||||
}
|
||||
|
7
src/main/java/ru/ulstu/repository/NewsRepository.java
Normal file
7
src/main/java/ru/ulstu/repository/NewsRepository.java
Normal file
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.model.News;
|
||||
|
||||
public interface NewsRepository extends JpaRepository<News, Integer> {
|
||||
}
|
49
src/main/java/ru/ulstu/service/NewsService.java
Normal file
49
src/main/java/ru/ulstu/service/NewsService.java
Normal file
@ -0,0 +1,49 @@
|
||||
package ru.ulstu.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.model.News;
|
||||
import ru.ulstu.repository.NewsRepository;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class NewsService {
|
||||
private final NewsRepository newsRepository;
|
||||
|
||||
public NewsService(NewsRepository newsRepository) {
|
||||
this.newsRepository = newsRepository;
|
||||
}
|
||||
|
||||
public void create(String title, String text) {
|
||||
newsRepository.save(new News(title, new Date(), text));
|
||||
}
|
||||
|
||||
public void create(News news) {
|
||||
news.setDate(new Date());
|
||||
newsRepository.save(news);
|
||||
}
|
||||
|
||||
public void save(News news) {
|
||||
if (news.getId() != null && (news.getId() != 0)) {
|
||||
newsRepository.save(news);
|
||||
} else {
|
||||
create(news);
|
||||
}
|
||||
}
|
||||
|
||||
public News getById(@NotNull Integer id) {
|
||||
return newsRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Новость не найдена"));
|
||||
}
|
||||
|
||||
public List<News> getAll() {
|
||||
return newsRepository.findAll();
|
||||
}
|
||||
|
||||
public void delete(Integer id) {
|
||||
newsRepository.deleteById(id);
|
||||
}
|
||||
}
|
@ -8,3 +8,11 @@ spring.main.banner-mode=off
|
||||
logging.level.tech.athene=DEBUG
|
||||
server.port=8080
|
||||
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
|
||||
# go to http://localhost:8080/h2-console
|
||||
spring.datasource.url=jdbc:h2:file:./data/db
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=password
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
spring.h2.console.enabled=true
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
|
@ -9,18 +9,26 @@
|
||||
}
|
||||
|
||||
.news-item {
|
||||
margin: 20px;
|
||||
position: relative;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.news-date {
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 25px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.news-image {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.link-dark, .link-dark:visited, .link-dark:focus {
|
||||
color: black;
|
||||
}
|
BIN
src/main/resources/static/favicon.ico
Normal file
BIN
src/main/resources/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
16
src/main/resources/templates/admin.html
Normal file
16
src/main/resources/templates/admin.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!--
|
||||
~ 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"
|
||||
layout:decorate="~{default}">
|
||||
<div class="container" layout:fragment="content">
|
||||
<a href="/editNews/0" class="btn btn-outline-dark">
|
||||
<i class="fa fa-plus-square" aria-hidden="true"> Добавить новость</i>
|
||||
</a>
|
||||
</div>
|
||||
</html>
|
@ -17,12 +17,12 @@
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/4.6.0/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-select/1.13.8/css/bootstrap-select.min.css"/>
|
||||
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
||||
<link rel="stylesheet" href="css/main.css"/>
|
||||
<link rel="stylesheet" href="/css/main.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-light bg-white">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="img/logo.svg" width="50px">
|
||||
<img src="/img/logo.svg" width="50px">
|
||||
<div class="navbar-text" th:text="#{messages.logo-title}" style="font-size: 16px"></div>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
|
||||
@ -32,13 +32,16 @@
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="news">Новости</a>
|
||||
<a class="nav-link" href="/news">Новости</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="news">Заседания</a>
|
||||
<a class="nav-link" href="/news">Заседания</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="news">Отчеты</a>
|
||||
<a class="nav-link" href="/news">Отчеты</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin">Администратору</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
34
src/main/resources/templates/editNews.html
Normal file
34
src/main/resources/templates/editNews.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
~ 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="@{/saveNews}" th:object="${news}" method="post">
|
||||
<input type="hidden" th:field="*{id}">
|
||||
<input type="hidden" th:field="*{date}">
|
||||
<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>
|
@ -8,23 +8,32 @@
|
||||
<html
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||
layout:decorate="~{default}">
|
||||
<head>
|
||||
<title>Time series smoothing</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
</head>
|
||||
<div class="container" layout:fragment="content">
|
||||
<div th:each="n : ${news}" class="news">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<img class="news-image" src="img/logo.svg"/>
|
||||
<img class="news-image" src="/img/logo.svg"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<h5 th:text="${n.title}"/>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<a th:href="@{'/news/' + ${n.id}}" class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||
</div>
|
||||
<div class="col-md-2" style="text-align: right">
|
||||
<a th:href="@{'/editNews/' + ${n.id}}" class="link-dark">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a th:href="@{'/deleteNews/' + ${n.id}}" class="link-dark"
|
||||
onclick="return confirm('Удалить новость?')">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${n.preview}" class="news-item"></div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||
class="news-date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||
class="news-date"></div>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
|
25
src/main/resources/templates/news.html
Normal file
25
src/main/resources/templates/news.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ 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="${news.title}"/>
|
||||
<div th:text="${news.text}" class="news-item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(news.date, 'dd.MM.yyyy HH:mm')}"
|
||||
class="news-date"></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