add rich text editor
This commit is contained in:
parent
4862f26258
commit
b5aee9c7fc
@ -48,6 +48,7 @@ dependencies {
|
||||
implementation group: 'org.webjars', name: 'font-awesome', version: '4.7.0'
|
||||
implementation group: 'org.webjars', name: 'momentjs', version: '2.24.0'
|
||||
implementation group: 'org.webjars', name: 'bootstrap-glyphicons', version: 'bdd2cbfba0'
|
||||
implementation group: 'org.webjars', name: 'summernote', version: '0.8.10'
|
||||
|
||||
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
||||
}
|
||||
|
@ -70,8 +70,7 @@ public class MeetingController {
|
||||
|
||||
@PostMapping("saveMeeting")
|
||||
@Secured({UserRoleConstants.ADMIN})
|
||||
public String saveNews(@Valid @ModelAttribute Meeting meeting,
|
||||
BindingResult result) {
|
||||
public String saveNews(@Valid @ModelAttribute Meeting meeting, BindingResult result) {
|
||||
if (result.hasErrors()) {
|
||||
return "editMeeting";
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
package ru.ulstu.news;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.ulstu.meeting.Meeting;
|
||||
import ru.ulstu.model.BaseEntity;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Transient;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.Date;
|
||||
|
||||
@ -26,11 +24,6 @@ public class News extends BaseEntity {
|
||||
@NotEmpty(message = "Текст новости не может быть пустым")
|
||||
private String text;
|
||||
|
||||
private String imageFileName;
|
||||
|
||||
@Transient
|
||||
private MultipartFile imageFile;
|
||||
|
||||
@OneToOne(mappedBy = "news")
|
||||
private Meeting meeting;
|
||||
|
||||
@ -67,22 +60,6 @@ public class News extends BaseEntity {
|
||||
return text;
|
||||
}
|
||||
|
||||
public String getImageFileName() {
|
||||
return imageFileName;
|
||||
}
|
||||
|
||||
public void setImageFileName(String imageFileName) {
|
||||
this.imageFileName = imageFileName;
|
||||
}
|
||||
|
||||
public MultipartFile getImageFile() {
|
||||
return imageFile;
|
||||
}
|
||||
|
||||
public void setImageFile(MultipartFile imageFile) {
|
||||
this.imageFile = imageFile;
|
||||
}
|
||||
|
||||
public Meeting getMeeting() {
|
||||
return meeting;
|
||||
}
|
||||
@ -90,10 +67,4 @@ public class News extends BaseEntity {
|
||||
public void setMeeting(Meeting meeting) {
|
||||
this.meeting = meeting;
|
||||
}
|
||||
|
||||
public String getPreview() {
|
||||
return text != null && text.length() > MAX_NEWS_TEXT_PREVIEW_LENGTH
|
||||
? text.substring(0, MAX_NEWS_TEXT_PREVIEW_LENGTH) + "..."
|
||||
: text;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import ru.ulstu.model.OffsetablePageRequest;
|
||||
import ru.ulstu.model.UserRoleConstants;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
@ -71,8 +70,7 @@ public class NewsController {
|
||||
|
||||
@PostMapping("saveNews")
|
||||
@Secured({UserRoleConstants.ADMIN})
|
||||
public String saveNews(@Valid @ModelAttribute News news,
|
||||
BindingResult result) throws IOException {
|
||||
public String saveNews(@Valid @ModelAttribute News news, BindingResult result) {
|
||||
if (result.hasErrors()) {
|
||||
return "editNews";
|
||||
}
|
||||
|
@ -3,13 +3,10 @@ package ru.ulstu.news;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.files.FileSystemStorageService;
|
||||
import ru.ulstu.files.FileUtil;
|
||||
import ru.ulstu.meeting.Meeting;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -37,15 +34,7 @@ public class NewsService {
|
||||
return newsRepository.save(news);
|
||||
}
|
||||
|
||||
public void save(News news) throws IOException {
|
||||
String fileName = System.currentTimeMillis() + "";
|
||||
if (!news.getImageFile().isEmpty()) {
|
||||
news.setImageFileName(fileName);
|
||||
FileUtil.saveFile(FileSystemStorageService.UPLOAD_DIR, fileName, news.getImageFile());
|
||||
} else {
|
||||
news.setImageFileName(news.getImageFileName().isEmpty() ? "logo.png" : news.getImageFileName());
|
||||
}
|
||||
|
||||
public void save(News news) {
|
||||
if (news.getId() != null && (news.getId() != 0)) {
|
||||
newsRepository.save(news);
|
||||
} else {
|
||||
|
@ -3,8 +3,11 @@ spring.main.banner-mode=off
|
||||
logging.level.tech.athene=DEBUG
|
||||
server.port=8080
|
||||
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
|
||||
spring.servlet.multipart.max-file-size=100000000
|
||||
spring.servlet.multipart.max-request-size=100000000
|
||||
spring.servlet.multipart.max-file-size=2048MB
|
||||
spring.servlet.multipart.max-request-size=2048MB
|
||||
server.tomcat.max-http-form-post-size=2048MB
|
||||
#server.tomcat.max-swallow-size=2048MB
|
||||
#server.max-http-header-size=2048MB
|
||||
# go to http://localhost:8080/h2-console
|
||||
spring.datasource.url=jdbc:h2:file:./data/db
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
|
@ -6,6 +6,10 @@
|
||||
<meta charset="UTF-8"/>
|
||||
<title th:text="#{messages.app-name}">app-name</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<script type="text/javascript" src="/webjars/jquery/3.6.0/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/4.3.0/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
||||
<link rel="stylesheet" href="/css/main.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="body-container">
|
||||
@ -83,9 +87,7 @@
|
||||
<div><img src="https://mc.yandex.ru/watch/88139282" style="position:absolute; left:-9999px;" alt=""/></div>
|
||||
</noscript>
|
||||
<!-- /Yandex.Metrika counter -->
|
||||
<script type="text/javascript" src="/webjars/jquery/3.6.0/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/4.3.0/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/css/main.css"/>
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
2022
|
||||
|
@ -36,10 +36,12 @@
|
||||
<script type="text/javascript" src="/webjars/bootstrap-select/1.13.8/js/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="/webjars/bootstrap-datetimepicker/2.4.4/js/bootstrap-datetimepicker.js"></script>
|
||||
<script type="text/javascript" src="/webjars/summernote/0.8.10/summernote-bs4.js"></script>
|
||||
<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="/webjars/bootstrap-glyphicons/bdd2cbfba0/css/bootstrap-glyphicons.css"/>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-datetimepicker/2.4.4/css/bootstrap-datetimepicker.css"/>
|
||||
<link rel="stylesheet" href="/webjars/summernote/0.8.10/summernote-bs4.css"/>
|
||||
<script>
|
||||
$(function () {
|
||||
$("#date").datetimepicker({
|
||||
@ -47,7 +49,9 @@
|
||||
locale: 'ru'
|
||||
});
|
||||
});
|
||||
|
||||
$('#text').summernote({
|
||||
height: 300
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</html>
|
||||
|
@ -8,7 +8,6 @@
|
||||
<input type="hidden" th:field="*{id}">
|
||||
<input type="hidden" th:field="*{date}">
|
||||
<input type="hidden" th:field="*{version}">
|
||||
<input type="hidden" th:field="*{imageFileName}">
|
||||
<div class="form-group">
|
||||
<label for="title">Заголовок</label>
|
||||
<input type="text" class="form-control" id="title" th:field="*{title}" placeholder="Заголовок новости">
|
||||
@ -22,14 +21,7 @@
|
||||
<p th:if="${#fields.hasErrors('text')}" th:class="${#fields.hasErrors('text')}? error">
|
||||
Не может быть пустым</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="image">Изображение</label>
|
||||
<input type="file" name="imageFile" class="form-control" th:field="*{imageFile}" id="image"
|
||||
placeholder="Фото новости"/>
|
||||
|
||||
<p th:if="${#fields.hasErrors('imageFile')}" th:class="${#fields.hasErrors('imageFile')} ? error">
|
||||
Ошибка</p>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-dark">Сохранить</button>
|
||||
<a href="javascript:history.back()" class="btn btn-outline-dark">Отмена</a>
|
||||
</form>
|
||||
@ -39,9 +31,17 @@
|
||||
<script type="text/javascript" src="/webjars/bootstrap-select/1.13.8/js/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="/webjars/bootstrap-datetimepicker/2.4.4/js/bootstrap-datetimepicker.js"></script>
|
||||
<script type="text/javascript" src="/webjars/summernote/0.8.10/summernote-bs4.js"></script>
|
||||
<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="/webjars/bootstrap-glyphicons/bdd2cbfba0/css/bootstrap-glyphicons.css"/>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-datetimepicker/2.4.4/css/bootstrap-datetimepicker.css"/>
|
||||
<link rel="stylesheet" href="/webjars/summernote/0.8.10/summernote-bs4.css"/>
|
||||
<script>
|
||||
$('#text').summernote({
|
||||
height: 300
|
||||
});
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</html>
|
||||
|
@ -4,10 +4,7 @@
|
||||
<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" th:src="@{'/files/' + ${n.imageFileName}}"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<a th:if="${n.meeting == null}" th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5
|
||||
@ -16,13 +13,13 @@
|
||||
class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${n.meeting == null}" th:text="${n.preview}" class="news-item"></div>
|
||||
<div th:if="${n.meeting == null}" th:utext="${n.text}" class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}" th:text="${'Тема заседания: ' + n.meeting.title}"
|
||||
class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}"
|
||||
th:text="${'Дата: ' + #calendars.format(n.meeting.date, 'dd.MM.yyyy HH:mm')}"
|
||||
class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}" th:text="${n.meeting.text}" class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}" th:utext="${n.meeting.text}" class="news-item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||
|
@ -7,6 +7,7 @@
|
||||
<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 th:text="${'Дата заседания: ' + #calendars.format(m.date, 'dd.MM.yyyy HH:mm')}"></div>
|
||||
</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">
|
||||
@ -29,6 +30,5 @@
|
||||
th:text=${pageNumber}
|
||||
th:class="${pageNumber == meetings.number+1} ? active"></a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</html>
|
||||
|
@ -5,10 +5,7 @@
|
||||
<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" th:src="@{'/files/' + ${n.imageFileName}}"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<a th:if="${n.meeting == null}" th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5
|
||||
@ -26,13 +23,13 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${n.meeting == null}" th:text="${n.preview}" class="news-item"></div>
|
||||
<div th:if="${n.meeting == null}" th:utext="${n.text}" class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}" th:text="${'Тема заседания: ' + n.meeting.title}"
|
||||
class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}"
|
||||
th:text="${'Дата: ' + #calendars.format(n.meeting.date, 'dd.MM.yyyy HH:mm')}"
|
||||
class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}" th:text="${n.meeting.text}" class="news-item"></div>
|
||||
<div th:if="${n.meeting != null}" th:utext="${n.meeting.text}" class="news-item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||
|
@ -4,12 +4,9 @@
|
||||
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">
|
||||
<div class="col-md-12">
|
||||
<h5 th:text="${meeting.title}"/>
|
||||
<div th:text="${meeting.text}" class="news-item"></div>
|
||||
<div th:utext="${meeting.text}" class="news-item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="javascript:history.back()" class="btn btn-outline-dark" style="text-align: right">Назад</a>
|
||||
|
@ -4,12 +4,9 @@
|
||||
layout:decorate="~{default}">
|
||||
<div class="container" layout:fragment="content">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<img class="news-image" th:src="@{'/files/' + ${news.imageFileName}}"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-12">
|
||||
<h5 th:text="${news.title}"/>
|
||||
<div th:text="${news.text}" class="news-item"></div>
|
||||
<div th:utext="${news.text}" class="news-item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(news.date, 'dd.MM.yyyy HH:mm')}"
|
||||
|
Loading…
Reference in New Issue
Block a user