Merge remote-tracking branch 'origin/master'
# Conflicts: # data/db.mv.db # src/main/resources/application.properties
This commit is contained in:
commit
305f78777c
29
build.gradle
29
build.gradle
@ -1,23 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||
*
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
|
||||
id 'org.springframework.boot' version '2.3.3.RELEASE'
|
||||
id "org.sonarqube" version "2.7"
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||
id 'org.springframework.boot' version '2.6.4'
|
||||
}
|
||||
|
||||
jar {
|
||||
archivesBaseName='seminar'
|
||||
archivesBaseName = 'seminar'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "http://repo.athene.tech/repository/maven-central/"
|
||||
allowInsecureProtocol(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,20 +33,23 @@ dependencies {
|
||||
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: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.1.0'
|
||||
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: '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.eclipse.jetty', name: 'jetty-servlet', version: versionJetty
|
||||
|
||||
implementation group: 'org.webjars', name: 'jquery', version: '3.6.0'
|
||||
implementation group: 'org.webjars', name: 'bootstrap', version: '4.6.0'
|
||||
implementation group: 'org.webjars', name: 'bootstrap', version: '4.3.0'
|
||||
implementation group: 'org.webjars', name: 'bootstrap-select', version: '1.13.8'
|
||||
implementation group: 'org.webjars', name: 'bootstrap-datetimepicker', version: '2.4.4'
|
||||
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'
|
||||
|
||||
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
||||
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
||||
}
|
||||
|
||||
|
||||
|
BIN
data/db.mv.db
BIN
data/db.mv.db
Binary file not shown.
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
|
@ -3,10 +3,12 @@ package ru.ulstu;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import ru.ulstu.user.UserService;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableConfigurationProperties
|
||||
public class SeminarApplication {
|
||||
private final UserService userService;
|
||||
|
||||
|
@ -24,6 +24,8 @@ public class MvcConfiguration implements WebMvcConfigurer {
|
||||
registry.addViewController("/loginError");
|
||||
registry.addViewController("/index");
|
||||
registry.addViewController("/admin");
|
||||
registry.addViewController("/organizers");
|
||||
registry.addViewController("/docs");
|
||||
registry.addViewController("/editNews");
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
log.debug("Security enabled");
|
||||
http.authorizeRequests()
|
||||
.antMatchers("/").permitAll()
|
||||
.antMatchers("/login", "/index", "/news/**", "/h2-console/*", "/h2-console").permitAll()
|
||||
.antMatchers("/login", "/index", "/news/**", "/meetings/**", "/files/**", "/docs/**",
|
||||
"/public/**", "/organizers", "/webjars/**", "/h2-console/*", "/h2-console").permitAll()
|
||||
.antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import nz.net.ultraq.thymeleaf.LayoutDialect;
|
||||
import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;
|
||||
|
@ -9,7 +9,7 @@ package ru.ulstu.controller;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import ru.ulstu.service.NewsService;
|
||||
import ru.ulstu.news.NewsService;
|
||||
|
||||
@Controller
|
||||
public class IndexController {
|
||||
|
80
src/main/java/ru/ulstu/files/FileSystemStorageService.java
Normal file
80
src/main/java/ru/ulstu/files/FileSystemStorageService.java
Normal file
@ -0,0 +1,80 @@
|
||||
package ru.ulstu.files;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Service
|
||||
public class FileSystemStorageService implements StorageService {
|
||||
public static final String UPLOAD_DIR = "upload";
|
||||
|
||||
private final Path rootLocation = Paths.get(UPLOAD_DIR);
|
||||
|
||||
@Override
|
||||
public void store(MultipartFile file) {
|
||||
try {
|
||||
if (file.isEmpty()) {
|
||||
throw new StorageException("Failed to store empty file " + file.getOriginalFilename());
|
||||
}
|
||||
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
|
||||
} catch (IOException e) {
|
||||
throw new StorageException("Failed to store file " + file.getOriginalFilename(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Path> loadAll() {
|
||||
try {
|
||||
return Files.walk(this.rootLocation, 1)
|
||||
.filter(path -> !path.equals(this.rootLocation))
|
||||
.map(path -> this.rootLocation.relativize(path));
|
||||
} catch (IOException e) {
|
||||
throw new StorageException("Failed to read stored files", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path load(String filename) {
|
||||
return rootLocation.resolve(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource loadAsResource(String filename) {
|
||||
try {
|
||||
Path file = load(filename);
|
||||
Resource resource = new UrlResource(file.toUri());
|
||||
if (resource.exists() || resource.isReadable()) {
|
||||
return resource;
|
||||
} else {
|
||||
throw new StorageFileNotFoundException("Could not read file: " + filename);
|
||||
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new StorageFileNotFoundException("Could not read file: " + filename, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
FileSystemUtils.deleteRecursively(rootLocation.toFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Files.createDirectory(rootLocation);
|
||||
} catch (IOException e) {
|
||||
throw new StorageException("Could not initialize storage", e);
|
||||
}
|
||||
}
|
||||
}
|
29
src/main/java/ru/ulstu/files/FileUtil.java
Normal file
29
src/main/java/ru/ulstu/files/FileUtil.java
Normal file
@ -0,0 +1,29 @@
|
||||
package ru.ulstu.files;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
public class FileUtil {
|
||||
|
||||
public static void saveFile(String uploadDir, String fileName,
|
||||
MultipartFile multipartFile) throws IOException {
|
||||
Path uploadPath = Paths.get(uploadDir);
|
||||
|
||||
if (!Files.exists(uploadPath)) {
|
||||
Files.createDirectories(uploadPath);
|
||||
}
|
||||
|
||||
try (InputStream inputStream = multipartFile.getInputStream()) {
|
||||
Path filePath = uploadPath.resolve(fileName);
|
||||
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException ioe) {
|
||||
throw new IOException("Could not save image file: " + fileName, ioe);
|
||||
}
|
||||
}
|
||||
}
|
28
src/main/java/ru/ulstu/files/FilesController.java
Normal file
28
src/main/java/ru/ulstu/files/FilesController.java
Normal file
@ -0,0 +1,28 @@
|
||||
package ru.ulstu.files;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
public class FilesController {
|
||||
private final StorageService storageService;
|
||||
|
||||
public FilesController(StorageService storageService) {
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
@GetMapping("/files/{filename:.+}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
|
||||
Resource file = storageService.loadAsResource((filename == null || filename.equals("null") || filename.isEmpty())
|
||||
? "logo.png"
|
||||
: filename);
|
||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"filename=\"" + file.getFilename() + "\"").body(file);
|
||||
}
|
||||
}
|
12
src/main/java/ru/ulstu/files/StorageException.java
Normal file
12
src/main/java/ru/ulstu/files/StorageException.java
Normal file
@ -0,0 +1,12 @@
|
||||
package ru.ulstu.files;
|
||||
|
||||
public class StorageException extends RuntimeException {
|
||||
|
||||
public StorageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StorageException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package ru.ulstu.files;
|
||||
|
||||
public class StorageFileNotFoundException extends StorageException {
|
||||
|
||||
public StorageFileNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StorageFileNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
23
src/main/java/ru/ulstu/files/StorageService.java
Normal file
23
src/main/java/ru/ulstu/files/StorageService.java
Normal file
@ -0,0 +1,23 @@
|
||||
package ru.ulstu.files;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface StorageService {
|
||||
|
||||
void init();
|
||||
|
||||
void store(MultipartFile file);
|
||||
|
||||
Stream<Path> loadAll();
|
||||
|
||||
Path load(String filename);
|
||||
|
||||
Resource loadAsResource(String filename);
|
||||
|
||||
void deleteAll();
|
||||
|
||||
}
|
85
src/main/java/ru/ulstu/meeting/Meeting.java
Normal file
85
src/main/java/ru/ulstu/meeting/Meeting.java
Normal file
@ -0,0 +1,85 @@
|
||||
package ru.ulstu.meeting;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import ru.ulstu.model.BaseEntity;
|
||||
import ru.ulstu.news.News;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Transient;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
public class Meeting extends BaseEntity {
|
||||
@NotEmpty(message = "Заголовок не может быть пустым")
|
||||
private String title;
|
||||
|
||||
@DateTimeFormat(pattern = "dd.MM.yyyy HH:mm")
|
||||
private Date date;
|
||||
|
||||
@Lob
|
||||
@NotEmpty(message = "Текст заседания не может быть пустым")
|
||||
private String text;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "news_id", referencedColumnName = "id")
|
||||
private News news;
|
||||
|
||||
@Transient
|
||||
private Integer newsId;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public News getNews() {
|
||||
return news;
|
||||
}
|
||||
|
||||
public void setNews(News news) {
|
||||
this.news = news;
|
||||
}
|
||||
|
||||
public Integer getNewsId() {
|
||||
return newsId == null
|
||||
? (news != null) ? news.getId() : null
|
||||
: newsId;
|
||||
}
|
||||
|
||||
public void setNewsId(Integer newsId) {
|
||||
this.newsId = newsId;
|
||||
}
|
||||
}
|
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);
|
||||
}
|
58
src/main/java/ru/ulstu/meeting/MeetingService.java
Normal file
58
src/main/java/ru/ulstu/meeting/MeetingService.java
Normal file
@ -0,0 +1,58 @@
|
||||
package ru.ulstu.meeting;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.news.News;
|
||||
import ru.ulstu.news.NewsService;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
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;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Meeting create(Meeting meeting) {
|
||||
meeting = meetingRepository.save(meeting);
|
||||
News news = newsService.create("Объявление о заседании семинара", meeting);
|
||||
meeting.setNews(news);
|
||||
return save(meeting);
|
||||
}
|
||||
|
||||
public Meeting save(Meeting meeting) {
|
||||
if (meeting.getNewsId() != null) {
|
||||
meeting.setNews(newsService.getById(meeting.getNewsId()));
|
||||
}
|
||||
return (meeting.getId() != null && (meeting.getId() != 0))
|
||||
? meetingRepository.save(meeting)
|
||||
: create(meeting);
|
||||
}
|
||||
|
||||
public Meeting getById(@NotNull Integer id) {
|
||||
return meetingRepository
|
||||
.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Запись о заседании не найдена"));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(Integer id) {
|
||||
Meeting meeting = meetingRepository.getById(id);
|
||||
if (meeting.getNews() != null) {
|
||||
newsService.delete(meeting.getNews().getId());
|
||||
}
|
||||
meetingRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public Page<Meeting> getMeetings(Pageable pageable) {
|
||||
return meetingRepository.findByOrderByDateDesc(pageable);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
public class OffsetablePageRequest implements Pageable, Serializable {
|
||||
private final int offset;
|
||||
@ -31,11 +32,26 @@ public class OffsetablePageRequest implements Pageable, Serializable {
|
||||
return sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort getSortOr(Sort sort) {
|
||||
return Pageable.super.getSortOr(sort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPaged() {
|
||||
return Pageable.super.isPaged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpaged() {
|
||||
return Pageable.super.isUnpaged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageNumber() {
|
||||
return offset / count;
|
||||
@ -51,6 +67,11 @@ public class OffsetablePageRequest implements Pageable, Serializable {
|
||||
return offset > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Pageable> toOptional() {
|
||||
return Pageable.super.toOptional();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable next() {
|
||||
return new OffsetablePageRequest(getOffset() + getPageSize(), getPageSize(), getSort());
|
||||
@ -70,6 +91,11 @@ public class OffsetablePageRequest implements Pageable, Serializable {
|
||||
return new OffsetablePageRequest(0, getPageSize(), getSort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable withPage(int pageNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
|
@ -1,9 +1,14 @@
|
||||
package ru.ulstu.model;
|
||||
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;
|
||||
|
||||
@ -21,6 +26,14 @@ public class News extends BaseEntity {
|
||||
@NotEmpty(message = "Текст новости не может быть пустым")
|
||||
private String text;
|
||||
|
||||
private String imageFileName;
|
||||
|
||||
@Transient
|
||||
private MultipartFile imageFile;
|
||||
|
||||
@OneToOne(mappedBy = "news")
|
||||
private Meeting meeting;
|
||||
|
||||
public News() {
|
||||
}
|
||||
|
||||
@ -54,6 +67,30 @@ 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;
|
||||
}
|
||||
|
||||
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) + "..."
|
@ -4,9 +4,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.ulstu.controller;
|
||||
package ru.ulstu.news;
|
||||
|
||||
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;
|
||||
@ -16,11 +17,11 @@ 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.News;
|
||||
import ru.ulstu.model.OffsetablePageRequest;
|
||||
import ru.ulstu.service.NewsService;
|
||||
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;
|
||||
@ -56,6 +57,7 @@ public class NewsController {
|
||||
}
|
||||
|
||||
@GetMapping("/editNews/{newsId}")
|
||||
@Secured({UserRoleConstants.ADMIN})
|
||||
public String editNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
||||
model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News());
|
||||
return "editNews";
|
||||
@ -68,16 +70,18 @@ public class NewsController {
|
||||
}
|
||||
|
||||
@PostMapping("saveNews")
|
||||
public String saveNews(@Valid @ModelAttribute News news, BindingResult result) {
|
||||
@Secured({UserRoleConstants.ADMIN})
|
||||
public String saveNews(@Valid @ModelAttribute News news,
|
||||
BindingResult result) throws IOException {
|
||||
if (result.hasErrors()) {
|
||||
return "editNews";
|
||||
}
|
||||
newsService.save(news);
|
||||
|
||||
return "redirect:/news/news";
|
||||
}
|
||||
|
||||
@GetMapping("deleteNews/{newsId}")
|
||||
@Secured({UserRoleConstants.ADMIN})
|
||||
public String delete(@PathVariable(value = "newsId") Integer id) {
|
||||
newsService.delete(id);
|
||||
return "redirect:/news/news";
|
@ -1,9 +1,8 @@
|
||||
package ru.ulstu.repository;
|
||||
package ru.ulstu.news;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.model.News;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -1,12 +1,15 @@
|
||||
package ru.ulstu.service;
|
||||
package ru.ulstu.news;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.model.News;
|
||||
import ru.ulstu.repository.NewsRepository;
|
||||
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;
|
||||
|
||||
@ -19,15 +22,30 @@ public class NewsService {
|
||||
}
|
||||
|
||||
public void create(String title, String text) {
|
||||
newsRepository.save(new News(title, new Date(), text));
|
||||
create(new News(title, new Date(), text));
|
||||
}
|
||||
|
||||
public void create(News news) {
|
||||
@Transactional
|
||||
public News create(String title, Meeting meeting) {
|
||||
News news = new News(title, new Date(), "Новость о заседении семинара");
|
||||
news.setMeeting(meeting);
|
||||
return create(news);
|
||||
}
|
||||
|
||||
public News create(News news) {
|
||||
news.setDate(new Date());
|
||||
newsRepository.save(news);
|
||||
return newsRepository.save(news);
|
||||
}
|
||||
|
||||
public void save(News 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());
|
||||
}
|
||||
|
||||
if (news.getId() != null && (news.getId() != 0)) {
|
||||
newsRepository.save(news);
|
||||
} else {
|
@ -2,6 +2,7 @@ package ru.ulstu.user;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@ -24,6 +25,8 @@ public class UserService implements UserDetailsService {
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final UserRepository userRepository;
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
@Value("${admin-password}")
|
||||
private String adminPassword;
|
||||
|
||||
public UserService(PasswordEncoder passwordEncoder,
|
||||
UserRepository userRepository,
|
||||
@ -63,7 +66,6 @@ public class UserService implements UserDetailsService {
|
||||
|
||||
public void initDefaultAdmin() {
|
||||
String adminLogin = "admin";
|
||||
String adminPassword = "adminadmin";
|
||||
if (getUserByLogin(adminLogin) == null) {
|
||||
UserRole adminRole = userRoleRepository.save(new UserRole(UserRoleConstants.ADMIN));
|
||||
createUser(new User(adminLogin, adminPassword, Set.of(adminRole)));
|
||||
|
@ -1,7 +1,10 @@
|
||||
admin-password=admin
|
||||
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
|
||||
# go to http://localhost:8080/h2-console
|
||||
spring.datasource.url=jdbc:h2:file:./data/db
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
|
BIN
src/main/resources/public/docs/plan2022.docx
Normal file
BIN
src/main/resources/public/docs/plan2022.docx
Normal file
Binary file not shown.
BIN
src/main/resources/public/docs/polozh.docx
Normal file
BIN
src/main/resources/public/docs/polozh.docx
Normal file
Binary file not shown.
@ -4,5 +4,8 @@
|
||||
<a href="/news/editNews/0" class="btn btn-outline-dark">
|
||||
<i class="fa fa-plus-square" aria-hidden="true">Добавить новость</i>
|
||||
</a>
|
||||
<a href="/meetings/editMeeting/0" class="btn btn-outline-dark">
|
||||
<i class="fa fa-plus-square" aria-hidden="true">Добавить заседание</i>
|
||||
</a>
|
||||
</div>
|
||||
</html>
|
||||
|
@ -7,11 +7,17 @@
|
||||
<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>
|
||||
<script type="text/javascript" src="/webjars/bootstrap/4.6.0/js/bootstrap.bundle.min.js"></script>
|
||||
<script type="text/javascript" src="/webjars/momentjs/2.24.0/moment.js"></script>
|
||||
<script type="text/javascript" src="/webjars/momentjs/2.24.0/locale/ru.js"></script>
|
||||
<script type="text/javascript" src="/webjars/bootstrap/4.3.0/js/bootstrap.bundle.min.js"></script>
|
||||
<script type="text/javascript" src="/webjars/bootstrap-select/1.13.8/js/bootstrap-select.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/4.6.0/css/bootstrap.min.css"/>
|
||||
<script type="text/javascript"
|
||||
src="/webjars/bootstrap-datetimepicker/2.4.4/js/bootstrap-datetimepicker.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/4.3.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="/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="/css/main.css"/>
|
||||
</head>
|
||||
<body>
|
||||
@ -30,10 +36,16 @@
|
||||
<a class="nav-link" href="/news/news">Новости</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/news">Заседания</a>
|
||||
<a class="nav-link" href="/meetings/meetings">Заседания</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/news">Отчеты</a>
|
||||
<a class="nav-link" href="/organizers">Организаторы</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/docs">Документы</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/reports">Отчеты</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin">Администратору</a>
|
||||
|
23
src/main/resources/templates/docs.html
Normal file
23
src/main/resources/templates/docs.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" layout:decorate="~{default}">
|
||||
<div class="container" layout:fragment="content">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<a class="link-secondary" href="/docs/polozh.docx">
|
||||
Положение
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<a class="link-secondary" href="/docs/plan2022.docx">
|
||||
План заседаний на 2022 год.
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</html>
|
42
src/main/resources/templates/editMeeting.html
Normal file
42
src/main/resources/templates/editMeeting.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!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}">
|
||||
<input type="hidden" th:field="*{newsId}">
|
||||
<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>
|
||||
<div class="form-group">
|
||||
<label for="date">Дата заседания</label>
|
||||
<input type="text" id="date" class="form-control" data-target="#date" th:field="*{date}"/>
|
||||
<p th:if="${#fields.hasErrors('date')}" th:class="${#fields.hasErrors('date')}? error">
|
||||
Не может быть пустым</p>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-dark">Сохранить</button>
|
||||
<a href="javascript:history.back()" class="btn btn-outline-dark">Отмена</a>
|
||||
</form>
|
||||
<script>
|
||||
$(function () {
|
||||
$("#date").datetimepicker({
|
||||
format: 'dd.mm.yyyy hh:ii',
|
||||
locale: 'ru'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</html>
|
@ -4,10 +4,11 @@
|
||||
layout:decorate="~{default}">
|
||||
<div class="container" layout:fragment="content">
|
||||
<h3>Редактирование новости:</h3>
|
||||
<form action="#" th:action="@{/news/saveNews}" th:object="${news}" method="post">
|
||||
<form action="#" th:action="@{/news/saveNews}" th:object="${news}" method="post" enctype="multipart/form-data">
|
||||
<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="Заголовок новости">
|
||||
@ -21,6 +22,14 @@
|
||||
<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>
|
||||
|
@ -5,15 +5,24 @@
|
||||
<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" th:src="@{'/files/' + ${n.imageFileName}}"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<a th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||
<a th:if="${n.meeting == null}" th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5
|
||||
th:text="${n.title}"/></a>
|
||||
<a th:if="${n.meeting != null}" th:href="@{'/meetings/meetings/' + ${n.meeting.id}}"
|
||||
class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${n.preview}" class="news-item"></div>
|
||||
<div th:if="${n.meeting == null}" th:text="${n.preview}" 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>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||
|
34
src/main/resources/templates/meetings.html
Normal file
34
src/main/resources/templates/meetings.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!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>
|
@ -6,15 +6,18 @@
|
||||
<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" th:src="@{'/files/' + ${n.imageFileName}}"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<a th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||
<a th:if="${n.meeting == null}" th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5
|
||||
th:text="${n.title}"/></a>
|
||||
<a th:if="${n.meeting != null}" th:href="@{'/meetings/meetings/' + ${n.meeting.id}}"
|
||||
class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||
</div>
|
||||
<div sec:authorize="hasRole('ROLE_ADMIN')" class="col-md-2" style="text-align: right">
|
||||
<a th:href="@{'/news/editNews/' + ${n.id}}" class="link-dark">
|
||||
<a th:if="${n.meeting == null}" th:href="@{'/news/editNews/' + ${n.id}}" class="link-dark">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a th:href="@{'/news/deleteNews/' + ${n.id}}" class="link-dark"
|
||||
@ -23,7 +26,13 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div th:text="${n.preview}" class="news-item"></div>
|
||||
<div th:if="${n.meeting == null}" th:text="${n.preview}" 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>
|
||||
</div>
|
||||
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||
|
29
src/main/resources/templates/organizers.html
Normal file
29
src/main/resources/templates/organizers.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!--
|
||||
~ 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">
|
||||
<h3>Состав организаторов семинара</h3>
|
||||
<ul>
|
||||
<li>
|
||||
Ярушкина Н.Г., профессор, д.т.н., профессор
|
||||
</li>
|
||||
<li>
|
||||
Мошкин В.С., доцент, к.т.н.
|
||||
</li>
|
||||
<li>
|
||||
Романов А.А., зав. кафедрой, доцент, к.т.н., доцент
|
||||
</li>
|
||||
<li>
|
||||
Гуськов Г.Ю., доцент, к.т.н.
|
||||
</li>
|
||||
<li>
|
||||
Филиппов А.А., доцент, к.т.н.
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
@ -5,7 +5,7 @@
|
||||
<div class="container" layout:fragment="content">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<img class="news-image" src="/img/logo.svg"/>
|
||||
<img class="news-image" th:src="@{'/files/' + ${news.imageFileName}}"/>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<h5 th:text="${news.title}"/>
|
||||
|
BIN
upload/1647261445926
Normal file
BIN
upload/1647261445926
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
BIN
upload/1647262081164
Normal file
BIN
upload/1647262081164
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
58
upload/1647262775259
Normal file
58
upload/1647262775259
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
width="1000"
|
||||
height="1000"
|
||||
viewBox="0 0 500 500"
|
||||
xml:space="preserve"
|
||||
id="svg45"
|
||||
sodipodi:docname="IS_logo_black.svg"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview47"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
showgrid="false"
|
||||
showborder="false"
|
||||
inkscape:zoom="0.769"
|
||||
inkscape:cx="500"
|
||||
inkscape:cy="500"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="991"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg45" />
|
||||
<desc
|
||||
id="desc29">Created with Fabric.js 4.6.0</desc>
|
||||
<defs
|
||||
id="defs31">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</defs>
|
||||
<path
|
||||
id="path33"
|
||||
style="fill:#000000;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 614.48828 177.41406 C 585.03403 177.64615 555.102 180 528 180 C 509.268 180 474.11592 173.20281 457.41992 181.83789 C 441.12592 190.26483 422.34 188.47467 404 190.33789 C 365.3278 194.26683 326.787 197.16411 288 199.82031 C 261.38 201.64331 230.50279 201.95901 212.77539 226.25781 C 196.16547 249.02541 217.5264 250.08661 219.7832 270.00781 C 223.4616 302.47861 220 337.3084 220 370 C 220 424.688 216.64862 482.378 228.69922 536 C 234.13282 560.178 241.07448 585.21445 258.58008 603.81445 C 286.96128 633.96645 327.183 628.83584 364 632.33984 C 434.958 639.09184 507.266 629.02169 578 624.17969 C 620.728 621.25169 671.094 620.42538 710 600.35938 C 785.886 561.21938 770.028 472.564 770 402 C 769.978 343.7938 783.18837 263.01536 746.73438 213.03516 C 735.77437 198.01096 714.65742 193.13757 698.10742 187.56055 C 672.91842 179.07202 643.94253 177.18197 614.48828 177.41406 z M 451.28516 278.24414 C 508.18157 277.94477 575.51534 292.60888 617.99609 321.03906 C 665.52009 352.84446 673.38009 409.60016 625.99609 446.66016 C 609.19809 459.79816 583.47409 470.56595 561.99609 465.75195 C 553.62409 463.87395 546.58609 456.87805 537.99609 456.49805 C 522.09609 455.79605 512.77609 463.8217 495.99609 456.5957 C 476.36009 448.1397 462.80717 414.5508 471.20117 394.2168 C 474.36317 386.5588 491.70145 379.35103 497.93945 387.20703 C 508.38745 400.36503 494.40964 432.52702 516.18164 440.79102 C 530.60964 446.26702 534.58192 411.00817 555.66992 414.32617 C 563.02792 415.48417 559.99497 424.656 562.04297 430 C 565.00297 437.722 572.19609 445.56767 579.99609 448.51367 C 613.82609 461.29167 642.2743 406.694 629.9043 380 C 614.3683 346.4774 578.65809 326.98059 545.99609 313.90039 C 482.01609 288.27899 396.70598 263.8648 347.51758 330 C 318.46498 369.0616 331.97353 426.708 360.51953 462 C 409.14233 522.116 497.53009 557.79392 573.99609 553.91992 C 589.34209 553.14192 620.65517 534.20281 630.95117 522.88281 C 639.49717 513.48281 642.08522 495.18492 661.69922 503.79492 C 677.95722 510.93492 656.70214 530.07166 649.99414 535.34766 C 623.11814 556.49766 583.00409 562 549.99609 562 C 468.35409 562 372.36956 530.03 324.10156 460 C 268.98216 380.028 324.58909 293.80382 413.99609 280.85742 C 425.58059 279.17993 438.15521 278.31323 451.28516 278.24414 z "
|
||||
transform="scale(0.5)" />
|
||||
|
||||
<path
|
||||
id="path39"
|
||||
style="fill:#000000;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 668.33594 643.47656 C 662.50706 643.55654 656.40463 644.00634 650 644.84766 C 557.68 656.97566 466.83 659.8418 374 659.8418 C 334.445 659.8418 285.9012 644.14103 248 660.95703 C 236.5832 666.02303 220.35223 674.28205 214.67383 685.99805 C 208.78323 698.15205 227.05259 701.474 224.27539 714 C 219.13139 737.202 163.19081 780.43105 192.00977 796.62305 C 213.03631 808.43705 243.23832 802.31272 265.85352 808.63672 C 272.15212 810.39872 271.30588 817.50928 276.70508 819.36328 C 297.12228 826.37328 330.3592 820 352 820 L 522 820 C 564.108 820 614.34895 827.63112 655.62695 819.70312 C 662.58095 818.36712 661.85506 809.62887 670.03906 808.29688 C 720.71106 800.04488 767.32912 814.45956 813.70312 783.85156 C 829.97513 773.11156 793.83588 738.344 786.17188 726 C 755.44819 676.52238 724.68174 642.70346 668.33594 643.47656 z M 632.96094 696.08594 C 656.9772 696.33526 670.5987 710.58 684.6582 732 C 687.2762 735.988 699.90202 751.25736 696.16602 755.94336 C 692.75802 760.21936 680.742 757.31823 676 757.49023 C 655.976 758.22223 636.016 761.75419 616 761.99219 C 537.304 762.92819 456.724 769.21231 378 763.82031 C 355.2814 762.26631 332.628 759.24231 310 756.57031 C 305.7648 756.07031 289.0863 758.23892 287.8125 752.79492 C 284.9009 740.34892 309.79135 708.10719 322.00195 708.11719 C 423.72835 708.20119 520.668 711.33947 622 696.85547 C 625.88675 696.29997 629.53004 696.05032 632.96094 696.08594 z "
|
||||
transform="scale(0.5)" />
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
0
upload/1647262911771
Normal file
0
upload/1647262911771
Normal file
0
upload/1647262925015
Normal file
0
upload/1647262925015
Normal file
BIN
upload/logo.png
Normal file
BIN
upload/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
Loading…
Reference in New Issue
Block a user