diff --git a/build.gradle b/build.gradle index 309b542..e339ff5 100644 --- a/build.gradle +++ b/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' diff --git a/data/db.mv.db b/data/db.mv.db new file mode 100644 index 0000000..8fe3c2f Binary files /dev/null and b/data/db.mv.db differ diff --git a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java index 9716493..2599a53 100644 --- a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java @@ -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 diff --git a/src/main/java/ru/ulstu/controller/IndexController.java b/src/main/java/ru/ulstu/controller/IndexController.java index b90b594..d6f2fc0 100644 --- a/src/main/java/ru/ulstu/controller/IndexController.java +++ b/src/main/java/ru/ulstu/controller/IndexController.java @@ -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"; } } diff --git a/src/main/java/ru/ulstu/controller/NewsController.java b/src/main/java/ru/ulstu/controller/NewsController.java new file mode 100644 index 0000000..3de7221 --- /dev/null +++ b/src/main/java/ru/ulstu/controller/NewsController.java @@ -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:/"; + } +} diff --git a/src/main/java/ru/ulstu/model/BaseEntity.java b/src/main/java/ru/ulstu/model/BaseEntity.java new file mode 100644 index 0000000..7946ec0 --- /dev/null +++ b/src/main/java/ru/ulstu/model/BaseEntity.java @@ -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 { + @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; + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/model/News.java b/src/main/java/ru/ulstu/model/News.java index 57f7130..f4dd2c8 100644 --- a/src/main/java/ru/ulstu/model/News.java +++ b/src/main/java/ru/ulstu/model/News.java @@ -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; } diff --git a/src/main/java/ru/ulstu/repository/NewsRepository.java b/src/main/java/ru/ulstu/repository/NewsRepository.java new file mode 100644 index 0000000..3aaf6bf --- /dev/null +++ b/src/main/java/ru/ulstu/repository/NewsRepository.java @@ -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 { +} diff --git a/src/main/java/ru/ulstu/service/NewsService.java b/src/main/java/ru/ulstu/service/NewsService.java new file mode 100644 index 0000000..293d062 --- /dev/null +++ b/src/main/java/ru/ulstu/service/NewsService.java @@ -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 getAll() { + return newsRepository.findAll(); + } + + public void delete(Integer id) { + newsRepository.deleteById(id); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7d3a7d9..14058f2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -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 diff --git a/src/main/resources/public/css/main.css b/src/main/resources/public/css/main.css index 2e4d9cf..359a34c 100644 --- a/src/main/resources/public/css/main.css +++ b/src/main/resources/public/css/main.css @@ -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; } \ No newline at end of file diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..dca8f8d Binary files /dev/null and b/src/main/resources/static/favicon.ico differ diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html new file mode 100644 index 0000000..f1bcb60 --- /dev/null +++ b/src/main/resources/templates/admin.html @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index ab97906..819bd2a 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -17,12 +17,12 @@ - +