Merge branch 'dev' into move-to-jdk11

# Conflicts:
#	build.gradle
#	src/main/java/ru/ulstu/grant/model/GrantDto.java
#	src/main/java/ru/ulstu/grant/service/GrantService.java
#	src/main/java/ru/ulstu/paper/model/PaperDto.java
#	src/main/java/ru/ulstu/project/model/Project.java
pull/161/head
Anton Romanov 5 years ago
commit 7684bac74d

@ -1,16 +1,11 @@
image: ubuntu:18.04
cache:
key: "$CI_PROJECT_ID"
paths:
- .gradle/
image: romanov73/is:ng-tracker-container
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- apt-get install openjdk-8-jdk git -y
- service postgresql stop
- service postgresql start
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
@ -20,12 +15,14 @@ before_script:
build:
stage: build
script: ./gradlew assemble
cache:
key: "$CI_PROJECT_ID"
policy: push
paths:
- build
- .gradle
checkRun:
stage: test
script: ./gradlew bootRun -Dng-tracker.check-run=true
checkStyle:
stage: test
script: ./gradlew check -x test
deploy:
stage: deploy
@ -33,12 +30,6 @@ deploy:
- sh deploy/gdccloud/deploy.sh
only:
- dev
cache:
key: "$CI_PROJECT_ID"
policy: pull
paths:
- build
- .gradle
environment:
name: staging
url: http://193.110.3.124:8080

@ -0,0 +1,46 @@
## Краткое описание задачи
Что требуется сделать
## `Опционально` Список верстаемых страниц
Будут затронуты страницы:
* page1.html
* page2.html
* page3.html
## `Опционально` Список затрагиваемых модулей
При реализации задачи потребуется также реализовать методы контроллера
## `Опционально` Список реализуемых функций
После выполнения задачи станет доступным:
* просмотр `entity_name`
* редактирование `entity_name`
* валидация `entity_name`
## `Опционально` Сценарии работы
Сценарий просмотра:
1. Зайти на главную страницу приложения
2. Перейти в раздел `section_name`
3. Перейти к списку `entity_name`
4. Выбрать нужную `entity_name` и нажать на нее
Сценарий редактирования:
1. Зайти на главную страницу приложения
2. Перейти в раздел `section_name`
3. Перейти к списку `entity_name`
4. Выбрать нужную `entity_name` и нажать на нее
5. Внести нужные правки в поля и сохранить
## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы
* Сверстаны страницы page1.hml, page2.hml, page3.hml
* Реализован контроллер для обслуживания страниц
* Сохранение в БД еще не реализовано
* Валидация происходит по полям `field1, field2`
* Сценарий просмотра проверяется при ручном внечении записей в БД

@ -30,6 +30,10 @@ bootRun.dependsOn checkstyleMain
sourceCompatibility = 1.8
targetCompatibility = 1.8
bootRun {
systemProperties = System.properties
}
checkstyle {
project.ext.checkstyleVersion = '8.8'
@ -110,6 +114,7 @@ dependencies {
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.6.3'
compile group: 'com.mattbertolini', name: 'liquibase-slf4j', version: '2.0.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
compile group: 'org.webjars', name: 'bootstrap', version: '4.1.0'
@ -118,8 +123,10 @@ dependencies {
compile group: 'org.webjars.npm', name: 'jquery.easing', version: '1.4.1'
compile group: 'org.webjars', name: 'font-awesome', version: '4.7.0'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.5.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.0'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.6.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.6.0'
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
testCompile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.3.1'
}

@ -100,7 +100,7 @@
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<!--<module name="AvoidStarImport"/>-->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<!--module name="UnusedImports">
@ -136,7 +136,7 @@
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<!--<module name="NeedBraces"/>-->
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->

@ -2,13 +2,30 @@ package ru.ulstu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import ru.ulstu.configuration.ApplicationProperties;
import ru.ulstu.core.repository.JpaDetachableRepositoryImpl;
@SpringBootApplication
@EnableJpaRepositories(repositoryBaseClass = JpaDetachableRepositoryImpl.class)
public class NgTrackerApplication {
private final ApplicationProperties applicationProperties;
public NgTrackerApplication(ApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
}
public static void main(String[] args) {
SpringApplication.run(NgTrackerApplication.class, args);
}
@EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
System.out.println("hello world, I have just started up");
if (applicationProperties.isCheckRun()) {
System.exit(0);
}
}
}

@ -2,10 +2,35 @@ package ru.ulstu.conference.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors;
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.conference.model.ConferenceDto;
import ru.ulstu.conference.model.ConferenceFilterDto;
import ru.ulstu.conference.model.ConferenceUser;
import ru.ulstu.conference.service.ConferenceService;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.user.model.User;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.util.StringUtils.isEmpty;
import static ru.ulstu.core.controller.Navigation.CONFERENCES_PAGE;
import static ru.ulstu.core.controller.Navigation.CONFERENCE_PAGE;
import static ru.ulstu.core.controller.Navigation.REDIRECT_TO;
@Controller()
@RequestMapping(value = "/conferences")
@ApiIgnore
@ -13,7 +38,119 @@ public class ConferenceController {
private final ConferenceService conferenceService;
public ConferenceController(ConferenceService paperService) {
this.conferenceService = paperService;
public ConferenceController(ConferenceService conferenceService) {
this.conferenceService = conferenceService;
}
@GetMapping("/conferences")
public void getConferences(ModelMap modelMap) {
modelMap.put("filteredConferences", new ConferenceFilterDto(conferenceService.findAllDto()));
}
@PostMapping("/conferences")
public void filterConferences(@Valid ConferenceFilterDto conferenceFilterDto, ModelMap modelMap) {
modelMap.put("filteredConferences", new ConferenceFilterDto(conferenceService.filter(conferenceFilterDto),
conferenceFilterDto.getFilterUserId(),
conferenceFilterDto.getYear()));
}
@GetMapping("/dashboard")
public void getDashboard(ModelMap modelMap) {
modelMap.put("conferences", conferenceService.findAllActiveDto());
}
@GetMapping("/conference")
public void getConference(ModelMap modelMap, @RequestParam(value = "id") Integer id) {
if (id != null && id > 0) {
modelMap.put("conferenceDto", conferenceService.getExistConferenceById(id));
} else {
modelMap.put("conferenceDto", conferenceService.getNewConference());
}
}
@PostMapping(value = "/conference", params = "save")
public String save(@Valid ConferenceDto conferenceDto, Errors errors) throws IOException {
filterEmptyDeadlines(conferenceDto);
if (errors.hasErrors()) {
return CONFERENCE_PAGE;
}
conferenceService.save(conferenceDto);
return String.format(REDIRECT_TO, CONFERENCES_PAGE);
}
@GetMapping("/delete/{conference-id}")
public String delete(@PathVariable("conference-id") Integer conferenceId) throws IOException {
conferenceService.delete(conferenceId);
return String.format(REDIRECT_TO, CONFERENCES_PAGE);
}
@PostMapping(value = "/conference", params = "addDeadline")
public String addDeadline(@Valid ConferenceDto conferenceDto, Errors errors) {
filterEmptyDeadlines(conferenceDto);
if (errors.hasErrors()) {
return CONFERENCE_PAGE;
}
conferenceDto.getDeadlines().add(new Deadline());
return CONFERENCE_PAGE;
}
@PostMapping(value = "/conference", params = "removeDeadline")
public String removeDeadline(@Valid ConferenceDto conferenceDto, Errors errors,
@RequestParam(value = "removeDeadline") Integer deadlineIndex) throws IOException {
if (errors.hasErrors()) {
return CONFERENCE_PAGE;
}
conferenceService.removeDeadline(conferenceDto, deadlineIndex);
return CONFERENCE_PAGE;
}
@PostMapping(value = "/conference", params = "removePaper")
public String removePaper(@Valid ConferenceDto conferenceDto, Errors errors,
@RequestParam(value = "removePaper") Integer paperIndex) throws IOException {
if (errors.hasErrors()) {
return CONFERENCE_PAGE;
}
conferenceService.removePaper(conferenceDto, paperIndex);
return CONFERENCE_PAGE;
}
@PostMapping(value = "/conference", params = "takePart")
public String takePart(@Valid ConferenceDto conferenceDto, Errors errors) throws IOException {
if (errors.hasErrors()) {
return CONFERENCE_PAGE;
}
conferenceService.takePart(conferenceDto);
return CONFERENCE_PAGE;
}
@ModelAttribute("allParticipation")
public List<ConferenceUser.Participation> getAllParticipation() {
return conferenceService.getAllParticipations();
}
@ModelAttribute("allDeposit")
public List<ConferenceUser.Deposit> getAllDeposit() {
return conferenceService.getAllDeposit();
}
@ModelAttribute("allUsers")
public List<User> getAllUsers() {
return conferenceService.getAllUsers();
}
@ModelAttribute("allYears")
public List<Integer> getAllYears() {
List<Integer> years = new ArrayList<>();
for (int i = Calendar.getInstance().get(Calendar.YEAR); i > 2010; i--) {
years.add(i);
}
return years;
}
private void filterEmptyDeadlines(ConferenceDto conferenceDto) {
conferenceDto.setDeadlines(conferenceDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
.collect(Collectors.toList()));
}
}

@ -1,11 +1,138 @@
package ru.ulstu.conference.model;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.paper.model.Paper;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "conference")
public class Conference extends BaseEntity {
@NotBlank
private String title;
private String description;
private String url;
private int ping;
@Column(name = "begin_date")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date beginDate;
@Column(name = "end_date")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "conference_id", unique = true)
@Fetch(FetchMode.SUBSELECT)
@OrderBy("date")
private List<Deadline> deadlines = new ArrayList<>();
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "paper_conference",
joinColumns = {@JoinColumn(name = "conference_id")},
inverseJoinColumns = {@JoinColumn(name = "paper_id")})
private List<Paper> papers = new ArrayList<>();
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "conference_id", unique = true)
@Fetch(FetchMode.SUBSELECT)
private List<ConferenceUser> users = new ArrayList<>();
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPing() {
return ping;
}
public void setPing(int ping) {
this.ping = ping;
}
public Date getBeginDate() {
return beginDate;
}
public void setBeginDate(Date beginDate) {
this.beginDate = beginDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public List<Deadline> getDeadlines() {
return deadlines;
}
public void setDeadlines(List<Deadline> deadlines) {
this.deadlines = deadlines;
}
public List<Paper> getPapers() {
return papers;
}
public void setPapers(List<Paper> papers) {
this.papers = papers;
}
public List<ConferenceUser> getUsers() {
return users;
}
public void setUsers(List<ConferenceUser> users) {
this.users = users;
}
}

@ -1,4 +1,221 @@
package ru.ulstu.conference.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.paper.model.Paper;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static ru.ulstu.core.util.StreamApiUtils.convert;
public class ConferenceDto {
private final static String BEGIN_DATE = "Начало: ";
private final static String END_DATE = "Конец: ";
private Integer id;
@NotEmpty
@Size(min = 2, max = 400)
private String title;
@Size(max = 500)
private String description = "";
@Size(max = 255)
private String url = "";
private int ping = 0;
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date beginDate = new Date();
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate = new Date();
private List<Deadline> deadlines = new ArrayList<>();
private List<Integer> removedDeadlineIds = new ArrayList<>();
private List<Integer> userIds = new ArrayList<>();
private List<Integer> paperIds = new ArrayList<>();
private List<Paper> papers = new ArrayList<>();
private List<Paper> notSelectedPapers = new ArrayList<>();
private List<ConferenceUser> users = new ArrayList<>();
private boolean disabledTakePart = false;
public ConferenceDto() {
}
@JsonCreator
public ConferenceDto(@JsonProperty("id") Integer id,
@JsonProperty("title") String title,
@JsonProperty("description") String description,
@JsonProperty("url") String url,
@JsonProperty("ping") Integer ping,
@JsonProperty("beginDate") Date beginDate,
@JsonProperty("endDate") Date endDate,
@JsonProperty("deadlines") List<Deadline> deadlines,
@JsonProperty("userIds") List<Integer> userIds,
@JsonProperty("paperIds") List<Integer> paperIds,
@JsonProperty("users") List<ConferenceUser> users,
@JsonProperty("papers") List<Paper> papers,
@JsonProperty("notSelectedPapers") List<Paper> notSelectedPapers,
@JsonProperty("notSelectedPapers") Boolean disabledTakePart) {
this.id = id;
this.title = title;
this.description = description;
this.url = url;
this.ping = ping;
this.beginDate = beginDate;
this.endDate = endDate;
this.deadlines = deadlines;
this.userIds = userIds;
this.paperIds = paperIds;
this.users = users;
this.papers = papers;
this.notSelectedPapers = notSelectedPapers;
this.disabledTakePart = disabledTakePart;
}
public ConferenceDto(Conference conference) {
this.id = conference.getId();
this.title = conference.getTitle();
this.description = conference.getDescription();
this.url = conference.getUrl();
this.ping = conference.getPing();
this.beginDate = conference.getBeginDate();
this.endDate = conference.getEndDate();
this.deadlines = conference.getDeadlines();
this.userIds = convert(conference.getUsers(), user -> user.getId());
this.paperIds = convert(conference.getPapers(), paper -> paper.getId());
this.users = conference.getUsers();
this.papers = conference.getPapers();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPing() {
return ping;
}
public void setPing(int ping) {
this.ping = ping;
}
public Date getBeginDate() {
return beginDate;
}
public void setBeginDate(Date beginDate) {
this.beginDate = beginDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public List<Deadline> getDeadlines() {
return deadlines;
}
public void setDeadlines(List<Deadline> deadlines) {
this.deadlines = deadlines;
}
public List<Integer> getUserIds() {
return userIds;
}
public void setUserIds(List<Integer> userIds) {
this.userIds = userIds;
}
public List<Integer> getPaperIds() {
return paperIds;
}
public void setPaperIds(List<Integer> paperIds) {
this.paperIds = paperIds;
}
public List<Paper> getPapers() {
return papers;
}
public void setPapers(List<Paper> papers) {
this.papers = papers;
}
public List<ConferenceUser> getUsers() {
return users;
}
public void setUsers(List<ConferenceUser> users) {
this.users = users;
}
public boolean isDisabledTakePart() {
return disabledTakePart;
}
public void setDisabledTakePart(boolean disabledTakePart) {
this.disabledTakePart = disabledTakePart;
}
public List<Integer> getRemovedDeadlineIds() {
return removedDeadlineIds;
}
public void setRemovedDeadlineIds(List<Integer> removedDeadlineIds) {
this.removedDeadlineIds = removedDeadlineIds;
}
public List<Paper> getNotSelectedPapers() {
return notSelectedPapers;
}
public void setNotSelectedPapers(List<Paper> notSelectedPapers) {
this.notSelectedPapers = notSelectedPapers;
}
public String getDatesString() {
return BEGIN_DATE + beginDate.toString().split(" ")[0] + " " + END_DATE + endDate.toString().split(" ")[0];
}
}

@ -1,4 +1,47 @@
package ru.ulstu.conference.model;
import java.util.List;
public class ConferenceFilterDto {
private List<ConferenceDto> conferences;
private Integer filterUserId;
private Integer year;
public ConferenceFilterDto() {
}
public ConferenceFilterDto(List<ConferenceDto> conferenceDtos, Integer filterUserId, Integer year) {
this.conferences = conferenceDtos;
this.filterUserId = filterUserId;
this.year = year;
}
public ConferenceFilterDto(List<ConferenceDto> conferenceDtos) {
this(conferenceDtos, null, null);
}
public List<ConferenceDto> getConferences() {
return conferences;
}
public void setConferences(List<ConferenceDto> conferences) {
this.conferences = conferences;
}
public Integer getFilterUserId() {
return filterUserId;
}
public void setFilterUserId(Integer filterUserId) {
this.filterUserId = filterUserId;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
}

@ -0,0 +1,110 @@
package ru.ulstu.conference.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.user.model.User;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "users_conference")
public class ConferenceUser extends BaseEntity {
public enum Participation {
INTRAMURAL("Очная"),
EXTRAMURAL("Заочная");
private String participationName;
Participation(String name) {
this.participationName = name;
}
public String getParticipation() {
return participationName;
}
}
public enum Deposit {
ARTICLE("Статья"),
REPORT("Доклад"),
PRESENTATION("Презентация");
private String depositName;
Deposit(String name) {
this.depositName = name;
}
public String getDeposit() {
return depositName;
}
}
@NotNull
@Column(name = "participation", nullable = false)
@Enumerated(value = EnumType.STRING)
private Participation participation = Participation.INTRAMURAL;
@NotNull
@Column(name = "deposit", nullable = false)
@Enumerated(value = EnumType.STRING)
private Deposit deposit = Deposit.ARTICLE;
@ManyToOne(optional = false)
@JoinColumn(name = "users_id")
private User user;
public ConferenceUser() {
}
public ConferenceUser(User user) {
this.user = user;
}
@JsonCreator
public ConferenceUser(@JsonProperty("id") Integer id,
@JsonProperty("deposit") Participation participation,
@JsonProperty("deposit") Deposit deposit,
@JsonProperty("user") User user) {
this.setId(id);
this.participation = participation;
this.deposit = deposit;
this.user = user;
}
public Participation getParticipation() {
return participation;
}
public void setParticipation(Participation participation) {
this.participation = participation;
}
public Deposit getDeposit() {
return deposit;
}
public void setDeposit(Deposit deposit) {
this.deposit = deposit;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

@ -1,7 +1,19 @@
package ru.ulstu.conference.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.ulstu.conference.model.Conference;
import ru.ulstu.user.model.User;
import java.util.Date;
import java.util.List;
public interface ConferenceRepository extends JpaRepository<Conference, Integer> {
@Query("SELECT c FROM Conference c LEFT JOIN c.users u WHERE (:user IS NULL OR u.user = :user) " +
"AND (YEAR(c.beginDate) = :year OR :year IS NULL) ORDER BY begin_date DESC")
List<Conference> findByUserAndYear(@Param("user") User user, @Param("year") Integer year);
@Query("SELECT c FROM Conference c WHERE c.beginDate > :date")
List<Conference> findAllActive(@Param("date") Date date);
}

@ -0,0 +1,7 @@
package ru.ulstu.conference.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.conference.model.ConferenceUser;
public interface ConferenceUserRepository extends JpaRepository<ConferenceUser, Integer> {
}

@ -1,17 +1,176 @@
package ru.ulstu.conference.service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.ulstu.conference.model.Conference;
import ru.ulstu.conference.model.ConferenceDto;
import ru.ulstu.conference.model.ConferenceFilterDto;
import ru.ulstu.conference.model.ConferenceUser;
import ru.ulstu.conference.repository.ConferenceRepository;
import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.paper.model.Paper;
import ru.ulstu.paper.service.PaperService;
import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
@Service
public class ConferenceService {
private final static int MAX_DISPLAY_SIZE = 40;
private final ConferenceRepository conferenceRepository;
private final ConferenceUserService conferenceUserService;
private final DeadlineService deadlineService;
private final PaperService paperService;
private final UserService userService;
public ConferenceService(ConferenceRepository conferenceRepository,
DeadlineService deadlineService) {
ConferenceUserService conferenceUserService,
DeadlineService deadlineService,
PaperService paperService,
UserService userService) {
this.conferenceRepository = conferenceRepository;
this.conferenceUserService = conferenceUserService;
this.deadlineService = deadlineService;
this.paperService = paperService;
this.userService = userService;
}
public ConferenceDto getExistConferenceById(Integer id) {
ConferenceDto conferenceDto = findOneDto(id);
conferenceDto.setNotSelectedPapers(getNotSelectPapers(conferenceDto.getPaperIds()));
conferenceDto.setDisabledTakePart(isCurrentUserParticipant(conferenceDto.getUsers()));
return conferenceDto;
}
public ConferenceDto getNewConference() {
ConferenceDto conferenceDto = new ConferenceDto();
conferenceDto.setNotSelectedPapers(getNotSelectPapers(new ArrayList<Integer>()));
return conferenceDto;
}
public List<Conference> findAll() {
return conferenceRepository.findAll(new Sort(Sort.Direction.DESC, "beginDate"));
}
public List<ConferenceDto> findAllDto() {
List<ConferenceDto> conferences = convert(findAll(), ConferenceDto::new);
conferences.forEach(conferenceDto -> conferenceDto.setTitle(StringUtils.abbreviate(conferenceDto.getTitle(), MAX_DISPLAY_SIZE)));
return conferences;
}
public ConferenceDto findOneDto(Integer id) {
return new ConferenceDto(conferenceRepository.getOne(id));
}
public void save(ConferenceDto conferenceDto) throws IOException {
if (isEmpty(conferenceDto.getId())) {
create(conferenceDto);
} else {
update(conferenceDto);
}
}
@Transactional
public Integer create(ConferenceDto conferenceDto) throws IOException {
Conference newConference = copyFromDto(new Conference(), conferenceDto);
newConference = conferenceRepository.save(newConference);
return newConference.getId();
}
@Transactional
public Integer update(ConferenceDto conferenceDto) throws IOException {
Conference conference = conferenceRepository.getOne(conferenceDto.getId());
conferenceRepository.save(copyFromDto(conference, conferenceDto));
conferenceDto.getRemovedDeadlineIds().forEach(deadlineService::remove);
return conference.getId();
}
@Transactional
public void delete(Integer conferenceId) {
if (conferenceRepository.existsById(conferenceId)) {
conferenceRepository.deleteById(conferenceId);
}
}
public void removeDeadline(ConferenceDto conferenceDto, Integer deadlineIndex) throws IOException {
if (conferenceDto.getDeadlines().get(deadlineIndex).getId() != null) {
conferenceDto.getRemovedDeadlineIds().add(conferenceDto.getDeadlines().get(deadlineIndex).getId());
}
conferenceDto.getDeadlines().remove((int) deadlineIndex);
}
public void removePaper(ConferenceDto conferenceDto, Integer paperIndex) throws IOException {
Paper removedPaper = conferenceDto.getPapers().remove((int) paperIndex);
conferenceDto.getNotSelectedPapers().add(removedPaper);
}
public void takePart(ConferenceDto conferenceDto) throws IOException {
conferenceDto.getUsers().add(new ConferenceUser(userService.getCurrentUser()));
conferenceDto.setDisabledTakePart(true);
}
public List<Paper> getNotSelectPapers(List<Integer> paperIds) {
return paperService.findAllNotSelect(paperIds);
}
public List<User> getAllUsers() {
return userService.findAll();
}
public List<ConferenceUser.Participation> getAllParticipations() {
return Arrays.asList(ConferenceUser.Participation.values());
}
public List<ConferenceUser.Deposit> getAllDeposit() {
return Arrays.asList(ConferenceUser.Deposit.values());
}
private Conference copyFromDto(Conference conference, ConferenceDto conferenceDto) throws IOException {
conference.setTitle(conferenceDto.getTitle());
conference.setDescription(conferenceDto.getDescription());
conference.setUrl(conferenceDto.getUrl());
conference.setPing(0);
conference.setBeginDate(conferenceDto.getBeginDate());
conference.setEndDate(conferenceDto.getEndDate());
conference.setPapers(conferenceDto.getPapers());
conference.setDeadlines(deadlineService.saveOrCreate(conferenceDto.getDeadlines()));
conference.setUsers(conferenceUserService.saveOrCreate(conferenceDto.getUsers()));
if (conferenceDto.getPaperIds() != null && !conferenceDto.getPaperIds().isEmpty()) {
conferenceDto.getPaperIds().forEach(paperId ->
conference.getPapers().add(paperService.findEntityById(paperId)));
}
return conference;
}
public boolean isCurrentUserParticipant(List<ConferenceUser> conferenceUsers) {
return conferenceUsers.stream().anyMatch(participant -> participant.getUser().equals(userService.getCurrentUser()));
}
public List<ConferenceDto> filter(ConferenceFilterDto conferenceFilterDto) {
return convert(conferenceRepository.findByUserAndYear(
conferenceFilterDto.getFilterUserId() == null ? null : userService.findById(conferenceFilterDto.getFilterUserId()),
conferenceFilterDto.getYear()), ConferenceDto::new);
}
public List<ConferenceDto> findAllActiveDto() {
return convert(findAllActive(), ConferenceDto::new);
}
public List<Conference> findAllActive() {
return conferenceRepository.findAllActive(new Date());
}
}

@ -0,0 +1,48 @@
package ru.ulstu.conference.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.ulstu.conference.model.ConferenceUser;
import ru.ulstu.conference.repository.ConferenceUserRepository;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class ConferenceUserService {
private final ConferenceUserRepository conferenceUserRepository;
public ConferenceUserService(ConferenceUserRepository conferenceUserRepository) {
this.conferenceUserRepository = conferenceUserRepository;
}
public List<ConferenceUser> saveOrCreate(List<ConferenceUser> users) {
return users
.stream()
.map(user -> {
return user.getId() != null ? update(user) : create(user);
}).collect(Collectors.toList());
}
@Transactional
public ConferenceUser update(ConferenceUser user) {
ConferenceUser updateUser = conferenceUserRepository.getOne(user.getId());
updateUser.setDeposit(user.getDeposit());
updateUser.setParticipation(user.getParticipation());
conferenceUserRepository.save(updateUser);
return updateUser;
}
@Transactional
public ConferenceUser create(ConferenceUser user) {
ConferenceUser newUser = new ConferenceUser();
newUser.setDeposit(user.getDeposit());
newUser.setParticipation(user.getParticipation());
newUser.setUser(user.getUser());
newUser = conferenceUserRepository.save(newUser);
return newUser;
}
}

@ -12,12 +12,16 @@ import javax.validation.constraints.NotBlank;
public class ApplicationProperties {
@NotBlank
private String baseUrl;
@NotBlank
private String undeadUserLogin;
private boolean devMode;
private boolean useHttps;
private boolean checkRun;
public boolean isUseHttps() {
return useHttps;
}
@ -49,4 +53,12 @@ public class ApplicationProperties {
public void setDevMode(boolean devMode) {
this.devMode = devMode;
}
public boolean isCheckRun() {
return checkRun;
}
public void setCheckRun(boolean checkRun) {
this.checkRun = checkRun;
}
}

@ -20,7 +20,6 @@ import ru.ulstu.user.error.UserNotActivatedException;
import ru.ulstu.user.error.UserNotFoundException;
import ru.ulstu.user.error.UserPasswordsNotValidOrNotMatchException;
import ru.ulstu.user.error.UserResetKeyError;
import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService;
import java.util.Set;
@ -28,7 +27,6 @@ import java.util.stream.Collectors;
@ControllerAdvice
public class AdviceController {
private final static String USER_NAME_TEMPLATE = "%s %s %s";
private final Logger log = LoggerFactory.getLogger(AdviceController.class);
private final UserService userService;
@ -38,11 +36,7 @@ public class AdviceController {
@ModelAttribute("currentUser")
public String getCurrentUser() {
User user = userService.getCurrentUser();
return String.format(USER_NAME_TEMPLATE,
user.getLastName(),
user.getFirstName().substring(0, 1),
user.getPatronymic().substring(0, 1));
return userService.getCurrentUser().getUserAbbreviate();
}
private Response<Void> handleException(ErrorConstants error) {

@ -1,4 +1,4 @@
package ru.ulstu.grant.controller;
package ru.ulstu.core.controller;
import org.springframework.validation.Errors;
@ -7,6 +7,9 @@ public class Navigation {
public static final String GRANTS_PAGE = "/grants/grants";
public static final String GRANT_PAGE = "/grants/grant";
public static final String CONFERENCES_PAGE = "/conferences/conferences";
public static final String CONFERENCE_PAGE = "/conferences/conference";
public static String hasErrors(Errors errors, String page) {
if (errors.hasErrors()) {
return page;

@ -1,7 +0,0 @@
package ru.ulstu.core.error;
public class XlsLoadException extends Exception {
public XlsLoadException(String s) {
super(s);
}
}

@ -1,7 +0,0 @@
package ru.ulstu.core.error;
public class XlsParseException extends Exception {
public XlsParseException(String s) {
super(s);
}
}

@ -1,6 +1,10 @@
package ru.ulstu.core.model;
import javax.persistence.*;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import java.io.Serializable;
@MappedSuperclass

@ -1,31 +0,0 @@
package ru.ulstu.core.service;
import org.springframework.stereotype.Service;
import ru.ulstu.core.model.TreeDto;
import ru.ulstu.core.model.TreeEntity;
import java.util.List;
import java.util.function.Predicate;
@Service
public class TreeService<T extends TreeEntity> {
public TreeDto getTree(String rootName, List<T> rootItems) {
return addChildNode(new TreeDto(rootName), rootItems, element -> true);
}
public TreeDto getTree(String rootName, List<T> rootItems, Predicate<T> filterPredicate) {
return addChildNode(new TreeDto(rootName), rootItems, filterPredicate);
}
private TreeDto addChildNode(TreeDto currentRoot, List<T> children, Predicate<T> filterPredicate) {
if (children != null) {
children.stream()
.filter(filterPredicate)
.forEach(item -> {
TreeDto newNode = new TreeDto(item);
currentRoot.getChildren().add(addChildNode(newNode, item.getChildren(), filterPredicate));
});
}
return currentRoot;
}
}

@ -1,6 +1,11 @@
package ru.ulstu.core.util;
import java.time.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@ -24,7 +29,7 @@ public class DateUtils {
return cal;
}
public static List<Month> getMonths () {
public static List<Month> getMonths() {
return Arrays.asList(Month.values());
}

@ -41,4 +41,9 @@ public class DeadlineService {
newDeadline = deadlineRepository.save(newDeadline);
return newDeadline;
}
@Transactional
public void remove(Integer deadlineId) {
deadlineRepository.deleteById(deadlineId);
}
}

@ -19,6 +19,9 @@ public class FileData extends BaseEntity {
private byte[] data;
@Column(name = "is_latex_attach")
private Boolean isLatexAttach;
public String getName() {
return name;
}
@ -50,4 +53,12 @@ public class FileData extends BaseEntity {
public void setData(byte[] data) {
this.data = data;
}
public Boolean isLatexAttach() {
return isLatexAttach;
}
public void setLatexAttach(Boolean latexAttach) {
isLatexAttach = latexAttach;
}
}

@ -9,6 +9,7 @@ public class FileDataDto {
private String fileName;
private String tmpFileName;
private boolean deleted;
private Boolean isLatexAttach;
public FileDataDto() {
}
@ -16,17 +17,20 @@ public class FileDataDto {
@JsonCreator
public FileDataDto(@JsonProperty("id") Integer id,
@JsonProperty("name") String name,
@JsonProperty("isLatexAttach") Boolean isLatexAttach,
@JsonProperty("fileName") String fileName,
@JsonProperty("tmpFileName") String tmpFileName) {
this.id = id;
this.name = name;
this.fileName = fileName;
this.tmpFileName = tmpFileName;
this.isLatexAttach = isLatexAttach;
}
public FileDataDto(FileData fileData) {
this.id = fileData.getId();
this.name = fileData.getName();
this.isLatexAttach = fileData.isLatexAttach();
}
public FileDataDto(String fileName, String tmpFileName) {
@ -73,4 +77,19 @@ public class FileDataDto {
this.deleted = deleted;
}
public Boolean isLatexAttach() {
return isLatexAttach;
}
public Boolean getIsLatexAttach() {
return isLatexAttach;
}
public void setLatexAttach(Boolean latexAttach) {
isLatexAttach = latexAttach;
}
public void setIsLatexAttach(Boolean latexAttach) {
isLatexAttach = latexAttach;
}
}

@ -6,7 +6,10 @@ import org.springframework.web.multipart.MultipartFile;
import ru.ulstu.file.model.FileData;
import ru.ulstu.file.model.FileDataDto;
import ru.ulstu.file.repostory.FileRepository;
import ru.ulstu.paper.model.PaperDto;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -15,6 +18,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.UTF_8;
@ -94,7 +98,7 @@ public class FileService {
@Transactional
public FileData update(FileDataDto fileDataDto) {
FileData file = fileRepository.findOne(fileDataDto.getId());
FileData file = fileRepository.getOne(fileDataDto.getId());
return fileRepository.save(copyFromDto(file, fileDataDto));
}
@ -107,15 +111,39 @@ public class FileService {
private FileData copyFromDto(FileData fileData, FileDataDto fileDataDto) {
fileData.setName(fileDataDto.getName());
fileData.setLatexAttach(fileDataDto.isLatexAttach());
return fileData;
}
@Transactional
public void delete(Integer fileId) {
fileRepository.delete(fileRepository.findOne(fileId));
fileRepository.delete(fileRepository.getOne(fileId));
}
public FileDataDto createFromMultipartFile(MultipartFile multipartFile) throws IOException {
return new FileDataDto(multipartFile.getOriginalFilename(), uploadToTmpDir(multipartFile));
}
public void createLatexAttachs(PaperDto paper) throws IOException {
for (FileDataDto fileDataDto : paper.getFiles()
.stream()
.filter(f -> (f.isLatexAttach() != null && f.isLatexAttach()) && !f.isDeleted())
.collect(Collectors.toList())) {
if (fileDataDto.getId() == null) {
File oldFile = getTmpFilePath(fileDataDto.getTmpFileName()).toFile();
File renamed = getTmpFilePath(fileDataDto.getName()).toFile();
oldFile.renameTo(renamed);
} else {
Files.write(getTmpFilePath(fileDataDto.getName()), fileRepository.getOne(fileDataDto.getId()).getData());
}
}
}
public File createLatexFile(PaperDto paper) throws IOException {
BufferedWriter writer = Files.newBufferedWriter(getTmpFilePath(paper.getTitle() + ".tex"));
writer.write(paper.getLatexText());
writer.close();
return getTmpFilePath(paper.getTitle() + ".tex").toFile();
}
}

@ -13,6 +13,7 @@ import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.Grant;
import ru.ulstu.grant.model.GrantDto;
import ru.ulstu.grant.service.GrantService;
import ru.ulstu.user.model.User;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
@ -21,10 +22,9 @@ import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.util.StringUtils.isEmpty;
import static ru.ulstu.grant.controller.Navigation.GRANTS_PAGE;
import static ru.ulstu.grant.controller.Navigation.GRANT_PAGE;
import static ru.ulstu.grant.controller.Navigation.REDIRECT_TO;
import static ru.ulstu.grant.controller.Navigation.hasErrors;
import static ru.ulstu.core.controller.Navigation.GRANTS_PAGE;
import static ru.ulstu.core.controller.Navigation.GRANT_PAGE;
import static ru.ulstu.core.controller.Navigation.REDIRECT_TO;
@Controller()
@ -57,27 +57,42 @@ public class GrantController {
}
@PostMapping(value = "/grant", params = "save")
public String save(@Valid GrantDto grantDto, Errors errors) throws IOException {
public String save(@Valid GrantDto grantDto, Errors errors)
throws IOException {
filterEmptyDeadlines(grantDto);
if (grantDto.getDeadlines().isEmpty()) {
errors.rejectValue("deadlines", "errorCode", "Не может быть пустым");
errors.rejectValue("deadlines", "errorCode", "Не может быть пусто");
}
if (grantDto.getLeaderId().equals(-1)) {
errors.rejectValue("leaderId", "errorCode", "Укажите руководителя");
}
if (errors.hasErrors()) {
return GRANT_PAGE;
}
hasErrors(errors, GRANT_PAGE);
grantService.save(grantDto);
return String.format(REDIRECT_TO, GRANTS_PAGE);
}
@PostMapping(value = "/grant", params = "filterUsers")
public String filterUsers() {
return GRANT_PAGE;
}
@PostMapping(value = "/grant", params = "addDeadline")
public String addDeadline(@Valid GrantDto grantDto, Errors errors) {
filterEmptyDeadlines(grantDto);
hasErrors(errors, GRANT_PAGE);
if (errors.hasErrors()) {
return GRANT_PAGE;
}
grantDto.getDeadlines().add(new Deadline());
return GRANT_PAGE;
}
@PostMapping(value = "/grant", params = "createProject")
public String createProject(@Valid GrantDto grantDto, Errors errors) {
hasErrors(errors, GRANT_PAGE);
if (errors.hasErrors()) {
return GRANT_PAGE;
}
grantService.createProject(grantDto);
return GRANT_PAGE;
}
@ -93,6 +108,11 @@ public class GrantController {
return grantService.getGrantStatuses();
}
@ModelAttribute("allAuthors")
public List<User> getAllAuthors(GrantDto grantDto) {
return grantService.getGrantAuthors(grantDto);
}
private void filterEmptyDeadlines(GrantDto grantDto) {
grantDto.setDeadlines(grantDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))

@ -1,29 +1,36 @@
package ru.ulstu.grant.model;
import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.core.model.UserContainer;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.file.model.FileData;
import ru.ulstu.project.model.Project;
import ru.ulstu.user.model.User;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@Entity
@Table(name = "grants")
public class Grant extends BaseEntity {
public class Grant extends BaseEntity implements UserContainer {
public enum GrantStatus {
APPLICATION("Заявка"),
ON_COMPETITION("Отправлен на конкурс"),
@ -52,6 +59,7 @@ public class Grant extends BaseEntity {
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "grant_id")
@OrderBy("date")
private List<Deadline> deadlines = new ArrayList<>();
//Описание гранта
@ -67,6 +75,14 @@ public class Grant extends BaseEntity {
@JoinColumn(name = "project_id")
private Project project;
@ManyToMany(fetch = FetchType.EAGER)
private Set<User> authors = new HashSet<>();
@NotNull
@ManyToOne
@JoinColumn(name = "leader_id")
private User leader;
public GrantStatus getStatus() {
return status;
}
@ -115,6 +131,27 @@ public class Grant extends BaseEntity {
this.project = project;
}
public Set<User> getAuthors() {
return authors;
}
public void setAuthors(Set<User> authors) {
this.authors = authors;
}
@Override
public Set<User> getUsers() {
return getAuthors();
}
public User getLeader() {
return leader;
}
public void setLeader(User leader) {
this.leader = leader;
}
public Optional<Deadline> getNextDeadline() {
return deadlines
.stream()

@ -2,14 +2,22 @@ package ru.ulstu.grant.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.user.model.UserDto;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static ru.ulstu.core.util.StreamApiUtils.convert;
public class GrantDto {
private final static int MAX_AUTHORS_LENGTH = 60;
private Integer id;
@NotEmpty
private String title;
@ -18,6 +26,12 @@ public class GrantDto {
private String comment;
private String applicationFileName;
private ProjectDto project;
private Set<Integer> authorIds;
private Set<UserDto> authors;
private Integer leaderId;
private boolean wasLeader;
private boolean hasAge;
private boolean hasDegree;
public GrantDto() {
deadlines.add(new Deadline());
@ -29,7 +43,13 @@ public class GrantDto {
@JsonProperty("status") Grant.GrantStatus status,
@JsonProperty("deadlines") List<Deadline> deadlines,
@JsonProperty("comment") String comment,
@JsonProperty("project") ProjectDto project) {
@JsonProperty("project") ProjectDto project,
@JsonProperty("authorIds") Set<Integer> authorIds,
@JsonProperty("authors") Set<UserDto> authors,
@JsonProperty("leader") Integer leaderId,
@JsonProperty("wasLeader") boolean wasLeader,
@JsonProperty("hasAge") boolean hasAge,
@JsonProperty("hasDegree") boolean hasDegree) {
this.id = id;
this.title = title;
this.status = status;
@ -37,6 +57,11 @@ public class GrantDto {
this.comment = comment;
this.applicationFileName = null;
this.project = project;
this.authors = authors;
this.leaderId = leaderId;
this.wasLeader = wasLeader;
this.hasAge = hasAge;
this.hasDegree = hasDegree;
}
public GrantDto(Grant grant) {
@ -47,6 +72,12 @@ public class GrantDto {
this.comment = grant.getComment();
this.project = grant.getProject() == null ? null : new ProjectDto(grant.getProject());
this.applicationFileName = grant.getApplication() == null ? null : grant.getApplication().getName();
this.authorIds = convert(grant.getAuthors(), user -> user.getId());
this.authors = convert(grant.getAuthors(), UserDto::new);
this.leaderId = grant.getLeader().getId();
this.wasLeader = false;
this.hasAge = false;
this.hasDegree = false;
}
public Integer getId() {
@ -104,4 +135,59 @@ public class GrantDto {
public void setApplicationFileName(String applicationFileName) {
this.applicationFileName = applicationFileName;
}
public Set<Integer> getAuthorIds() {
return authorIds;
}
public void setAuthorIds(Set<Integer> authorIds) {
this.authorIds = authorIds;
}
public Set<UserDto> getAuthors() {
return authors;
}
public void setAuthors(Set<UserDto> authors) {
this.authors = authors;
}
public String getAuthorsString() {
return StringUtils.abbreviate(authors
.stream()
.map(author -> author.getLastName())
.collect(Collectors.joining(", ")), MAX_AUTHORS_LENGTH);
}
public Integer getLeaderId() {
return leaderId;
}
public void setLeaderId(Integer leaderId) {
this.leaderId = leaderId;
}
public boolean isWasLeader() {
return wasLeader;
}
public void setWasLeader(boolean wasLeader) {
this.wasLeader = wasLeader;
}
public boolean isHasAge() {
return hasAge;
}
public void setHasAge(boolean hasAge) {
this.hasAge = hasAge;
}
public boolean isHasDegree() {
return hasDegree;
}
public void setHasDegree(boolean hasDegree) {
this.hasDegree = hasDegree;
}
}

@ -1,19 +0,0 @@
package ru.ulstu.grant.model;
public class GrantStatusDto {
private final String id;
private final String name;
public GrantStatusDto(Grant.GrantStatus status) {
this.id = status.name();
this.name = status.getStatusName();
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}

@ -3,6 +3,9 @@ package ru.ulstu.grant.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.grant.model.Grant;
import java.util.List;
public interface GrantRepository extends JpaRepository<Grant, Integer> {
List<Grant> findByStatus(Grant.GrantStatus status);
}

@ -12,11 +12,14 @@ import ru.ulstu.grant.repository.GrantRepository;
import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.service.ProjectService;
import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
@ -30,15 +33,18 @@ public class GrantService {
private final ProjectService projectService;
private final DeadlineService deadlineService;
private final FileService fileService;
private final UserService userService;
public GrantService(GrantRepository grantRepository,
FileService fileService,
DeadlineService deadlineService,
ProjectService projectService) {
ProjectService projectService,
UserService userService) {
this.grantRepository = grantRepository;
this.projectService = projectService;
this.fileService = fileService;
this.deadlineService = deadlineService;
this.projectService = projectService;
this.userService = userService;
}
public List<Grant> findAll() {
@ -73,6 +79,13 @@ public class GrantService {
if (grantDto.getApplicationFileName() != null) {
grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName()));
}
grant.getAuthors().clear();
if (grantDto.getAuthorIds() != null && !grantDto.getAuthorIds().isEmpty()) {
grantDto.getAuthorIds().forEach(authorIds -> grant.getAuthors().add(userService.findById(authorIds)));
}
if (grantDto.getLeaderId() != null) {
grant.setLeader(userService.findById(grantDto.getLeaderId()));
}
return grant;
}
@ -98,7 +111,6 @@ public class GrantService {
if (grant.getApplication() != null) {
fileService.deleteFile(grant.getApplication());
}
//возможно при удалении гранта будет удаляться и проект, к нему привязанный
grantRepository.delete(grant);
}
@ -107,13 +119,15 @@ public class GrantService {
}
@Transactional
public Grant create(String title, Project projectId, Date deadlineDate) {
public Grant create(String title, Project projectId, Date deadlineDate, User user) {
Grant grant = new Grant();
grant.setTitle(title);
grant.setComment("Комментарий к гранту 1");
grant.setProject(projectId);
grant.setStatus(APPLICATION);
grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн"));
grant.getAuthors().add(user);
grant.setLeader(user);
grant = grantRepository.save(grant);
return grant;
}
@ -125,4 +139,22 @@ public class GrantService {
update(grantDto);
}
}
public List<User> getGrantAuthors(GrantDto grantDto) {
List<User> filteredUsers = userService.filterByAgeAndDegree(grantDto.isHasAge(), grantDto.isHasDegree());
if (grantDto.isWasLeader()) {
filteredUsers = filteredUsers
.stream()
.filter(getCompletedGrantLeaders()::contains)
.collect(Collectors.toList());
}
return filteredUsers;
}
private List<User> getCompletedGrantLeaders() {
return grantRepository.findByStatus(Grant.GrantStatus.COMPLETED)
.stream()
.map(Grant::getLeader)
.collect(Collectors.toList());
}
}

@ -31,14 +31,13 @@ public abstract class OdinField implements Comparable {
return this.name().toLowerCase();
}
}
private Field field;
protected final OdinFieldType fieldType;
protected final String fieldName;
protected final String caption;
protected final OdinVisible.OdinVisibleType visible;
protected final boolean readOnly;
protected final boolean notEmpty;
private Field field;
public OdinField(Field field, OdinFieldType fieldType) {
this.field = field;
@ -126,8 +125,12 @@ public abstract class OdinField implements Comparable {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OdinField odinField = (OdinField) o;
return Objects.equals(fieldName, odinField.fieldName);
}

@ -1,5 +1,8 @@
package ru.ulstu.paper.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors;
@ -13,17 +16,20 @@ import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.paper.model.Paper;
import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperFilterDto;
import ru.ulstu.paper.service.LatexService;
import ru.ulstu.paper.service.PaperService;
import ru.ulstu.user.model.User;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.util.StringUtils.isEmpty;
@ -32,9 +38,11 @@ import static org.springframework.util.StringUtils.isEmpty;
@ApiIgnore
public class PaperController {
private final PaperService paperService;
private final LatexService latexService;
public PaperController(PaperService paperService) {
public PaperController(PaperService paperService, LatexService latexService) {
this.paperService = paperService;
this.latexService = latexService;
}
@GetMapping("/papers")
@ -51,7 +59,7 @@ public class PaperController {
@GetMapping("/dashboard")
public void getDashboard(ModelMap modelMap) {
modelMap.put("papers", paperService.findAllActive());
modelMap.put("papers", paperService.findAllActiveDto());
}
@GetMapping("/paper")
@ -97,6 +105,11 @@ public class PaperController {
return paperService.getPaperStatuses();
}
@ModelAttribute("allTypes")
public List<Paper.PaperType> getPaperTypes() {
return paperService.getPaperTypes();
}
@ModelAttribute("allAuthors")
public List<User> getAllAuthors() {
return paperService.getPaperAuthors();
@ -111,6 +124,14 @@ public class PaperController {
return years;
}
@PostMapping("/generatePdf")
public ResponseEntity<byte[]> getPdfFile(PaperDto paper) throws IOException, InterruptedException {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename='" +
URLEncoder.encode(paper.getTitle() + ".pdf", UTF_8.toString()) + "'");
return new ResponseEntity<>(latexService.generatePdfFromLatexFile(paper), headers, HttpStatus.OK);
}
private void filterEmptyDeadlines(PaperDto paperDto) {
paperDto.setDeadlines(paperDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))

@ -61,4 +61,9 @@ public class PaperRestController {
public Response<List<PaperDto>> filter(@RequestBody @Valid PaperFilterDto paperFilterDto) throws IOException {
return new Response<>(paperService.filter(paperFilterDto));
}
@GetMapping("formatted-list")
public Response<List<String>> getFormattedPaperList() {
return new Response<>(paperService.getFormattedPaperList());
}
}

@ -53,12 +53,32 @@ public class Paper extends BaseEntity implements UserContainer {
}
}
public enum PaperType {
OTHER("Прочая публикация"),
VAK("ВАК"),
SCOPUS("Scopus"),
WEB_OF_SCIENCE("Web Of Science");
private String typeName;
PaperType(String name) {
this.typeName = name;
}
public String getTypeName() {
return typeName;
}
}
@NotBlank
private String title;
@Enumerated(value = EnumType.STRING)
private PaperStatus status = PaperStatus.DRAFT;
@Enumerated(value = EnumType.STRING)
private PaperType type = PaperType.OTHER;
@Column(name = "create_date")
@Temporal(TemporalType.TIMESTAMP)
private Date createDate = new Date();
@ -91,6 +111,9 @@ public class Paper extends BaseEntity implements UserContainer {
@ManyToMany(fetch = FetchType.EAGER)
private Set<User> authors = new HashSet<>();
@Column(name = "latex_text")
private String latexText;
public PaperStatus getStatus() {
return status;
}
@ -99,6 +122,14 @@ public class Paper extends BaseEntity implements UserContainer {
this.status = status;
}
public PaperType getType() {
return type;
}
public void setType(PaperType type) {
this.type = type;
}
public Date getCreateDate() {
return createDate;
}
@ -179,6 +210,14 @@ public class Paper extends BaseEntity implements UserContainer {
this.url = url;
}
public String getLatexText() {
return latexText;
}
public void setLatexText(String latexText) {
this.latexText = latexText;
}
@Override
public Set<User> getUsers() {
return getAuthors();

@ -3,8 +3,8 @@ package ru.ulstu.paper.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import ru.ulstu.file.model.FileDataDto;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.file.model.FileDataDto;
import ru.ulstu.user.model.UserDto;
import javax.validation.constraints.NotEmpty;
@ -25,6 +25,7 @@ public class PaperDto {
@Size(min = 3, max = 254)
private String title;
private Paper.PaperStatus status;
private Paper.PaperType type;
private Date createDate;
private Date updateDate;
@NotEmpty
@ -36,6 +37,7 @@ public class PaperDto {
private Set<Integer> authorIds;
private Set<UserDto> authors;
private Integer filterAuthorId;
private String latexText;
public PaperDto() {
deadlines.add(new Deadline());
@ -45,10 +47,12 @@ public class PaperDto {
public PaperDto(@JsonProperty("id") Integer id,
@JsonProperty("title") String title,
@JsonProperty("status") Paper.PaperStatus status,
@JsonProperty("type") Paper.PaperType type,
@JsonProperty("createDate") Date createDate,
@JsonProperty("updateDate") Date updateDate,
@JsonProperty("deadlines") List<Deadline> deadlines,
@JsonProperty("comment") String comment,
@JsonProperty("latex_text") String latexText,
@JsonProperty("url") String url,
@JsonProperty("locked") Boolean locked,
@JsonProperty("files") List<FileDataDto> files,
@ -57,11 +61,13 @@ public class PaperDto {
this.id = id;
this.title = title;
this.status = status;
this.type = type;
this.createDate = createDate;
this.updateDate = updateDate;
this.deadlines = deadlines;
this.comment = comment;
this.url = url;
this.latexText = latexText;
this.locked = locked;
this.files = files;
this.authors = authors;
@ -71,11 +77,13 @@ public class PaperDto {
this.id = paper.getId();
this.title = paper.getTitle();
this.status = paper.getStatus();
this.type = paper.getType();
this.createDate = paper.getCreateDate();
this.updateDate = paper.getUpdateDate();
this.deadlines = paper.getDeadlines();
this.comment = paper.getComment();
this.url = paper.getUrl();
this.latexText = paper.getLatexText();
this.locked = paper.getLocked();
this.files = convert(paper.getFiles(), FileDataDto::new);
this.authorIds = convert(paper.getAuthors(), user -> user.getId());
@ -106,6 +114,14 @@ public class PaperDto {
this.status = status;
}
public Paper.PaperType getType() {
return type;
}
public void setType(Paper.PaperType type) {
this.type = type;
}
public Date getCreateDate() {
return createDate;
}
@ -178,6 +194,14 @@ public class PaperDto {
this.url = url;
}
public String getLatexText() {
return latexText;
}
public void setLatexText(String latexText) {
this.latexText = latexText;
}
public String getAuthorsString() {
return StringUtils.abbreviate(authors
.stream()

@ -12,4 +12,6 @@ public interface PaperRepository extends JpaRepository<Paper, Integer> {
@Query("SELECT p FROM Paper p WHERE (:author IS NULL OR :author MEMBER OF p.authors) AND (YEAR(p.createDate) = :year OR :year IS NULL)")
List<Paper> filter(@Param("author") User author, @Param("year") Integer year);
List<Paper> findByIdNotIn(List<Integer> paperIds);
}

@ -0,0 +1,76 @@
package ru.ulstu.paper.service;
import org.springframework.stereotype.Service;
import ru.ulstu.file.service.FileService;
import ru.ulstu.paper.model.PaperDto;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
@Service
public class LatexService {
private final String pdfLatexError = "Errors occurred while executing pdfLaTeX.";
private final String bibtexError = "Errors occurred while executing bibtex.";
private String errorMessage;
private File pdfFile;
private FileService fileService;
public LatexService(FileService fileService) {
this.fileService = fileService;
}
public byte[] generatePdfFromLatexFile(PaperDto paper) throws IOException, InterruptedException {
fileService.createLatexAttachs(paper);
File tex = fileService.createLatexFile(paper);
if (!generate(paper.getTitle(), tex.getParentFile())) {
throw new IOException(errorMessage);
}
return Files.readAllBytes(pdfFile.toPath());
}
private int startProcess(String[] args, File dir, String message) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder(args);
processBuilder.redirectErrorStream(true);
processBuilder.directory(dir);
Process process = processBuilder.start();
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
while ((bufferedReader.readLine()) != null) {
//
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
errorMessage = message + " Exit value of the process: " + exitCode;
}
return exitCode;
}
private boolean generate(String filename, File dir) throws IOException, InterruptedException {
startProcess(new String[]{"pdflatex", filename, "--interaction=nonstopmode"}, dir, pdfLatexError);
startProcess(new String[]{"bibtex", filename}, dir, bibtexError);
if (startProcess(new String[]{"pdflatex", filename, "--interaction=nonstopmode"}, dir, pdfLatexError) != 0) {
return false;
}
return checkPdf(filename, dir);
}
private boolean checkPdf(String filename, File dir) {
pdfFile = new File(dir.getAbsolutePath() + File.separator + filename + ".pdf");
if (pdfFile.isFile()) {
return true;
} else {
errorMessage = "The pdf file could not be created.";
return false;
}
}
}

@ -21,6 +21,7 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
import static org.springframework.util.ObjectUtils.isEmpty;
@ -30,10 +31,12 @@ import static ru.ulstu.paper.model.Paper.PaperStatus.COMPLETED;
import static ru.ulstu.paper.model.Paper.PaperStatus.DRAFT;
import static ru.ulstu.paper.model.Paper.PaperStatus.FAILED;
import static ru.ulstu.paper.model.Paper.PaperStatus.ON_PREPARATION;
import static ru.ulstu.paper.model.Paper.PaperType.OTHER;
@Service
public class PaperService {
private final static int MAX_DISPLAY_SIZE = 40;
private final static String PAPER_FORMATTED_TEMPLATE = "%s %s";
private final PaperNotificationService paperNotificationService;
private final PaperRepository paperRepository;
@ -66,13 +69,17 @@ public class PaperService {
return papers;
}
public List<PaperDto> findAllActive() {
return findAllDto()
public List<Paper> findAllActive() {
return findAll()
.stream()
.filter(paper -> paper.getStatus() != COMPLETED && paper.getStatus() != FAILED)
.collect(toList());
}
public List<PaperDto> findAllActiveDto() {
return convert(findAllActive(), PaperDto::new);
}
public PaperDto findOneDto(Integer id) {
return new PaperDto(paperRepository.getOne(id));
}
@ -89,9 +96,11 @@ public class PaperService {
private Paper copyFromDto(Paper paper, PaperDto paperDto) throws IOException {
paper.setComment(paperDto.getComment());
paper.setUrl(paperDto.getUrl());
paper.setLatexText(paperDto.getLatexText());
paper.setCreateDate(paper.getCreateDate() == null ? new Date() : paper.getCreateDate());
paper.setLocked(paperDto.getLocked());
paper.setStatus(paperDto.getStatus() == null ? DRAFT : paperDto.getStatus());
paper.setType(paperDto.getType() == null ? OTHER : paperDto.getType());
paper.setTitle(paperDto.getTitle());
paper.setUpdateDate(new Date());
paper.setDeadlines(deadlineService.saveOrCreate(paperDto.getDeadlines()));
@ -142,6 +151,10 @@ public class PaperService {
return Arrays.asList(Paper.PaperStatus.values());
}
public List<Paper.PaperType> getPaperTypes() {
return Arrays.asList(Paper.PaperType.values());
}
@Transactional
public Paper create(String title, User user, Date deadlineDate) {
Paper paper = new Paper();
@ -151,6 +164,7 @@ public class PaperService {
paper.setCreateDate(new Date());
paper.setUpdateDate(new Date());
paper.setStatus(DRAFT);
paper.setType(OTHER);
paper = paperRepository.save(paper);
paperNotificationService.sendCreateNotification(paper);
@ -209,7 +223,41 @@ public class PaperService {
return new PaperDto(paperRepository.getOne(paperId));
}
public Paper findEntityById(Integer paperId) {
return paperRepository.getOne(paperId);
}
public List<Paper> findAllNotSelect(List<Integer> paperIds) {
if (!paperIds.isEmpty()) {
return sortPapers(paperRepository.findByIdNotIn(paperIds));
} else {
return sortPapers(paperRepository.findAll());
}
}
public List<User> getPaperAuthors() {
return userService.findAll();
}
public List<String> getFormattedPaperList() {
return findAllCompleted()
.stream()
.map(paper -> String.format(PAPER_FORMATTED_TEMPLATE, paper.getTitle(), getAuthors(paper)))
.collect(toList());
}
private List<Paper> findAllCompleted() {
return findAll()
.stream()
.filter(paper -> paper.getStatus() == COMPLETED)
.collect(toList());
}
private String getAuthors(Paper paper) {
return paper.getAuthors()
.stream()
.map(User::getUserAbbreviate)
.collect(Collectors.joining(", "));
}
}

@ -0,0 +1,49 @@
package ru.ulstu.project.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.service.ProjectService;
import springfox.documentation.annotations.ApiIgnore;
import java.util.List;
@Controller()
@RequestMapping(value = "/projects")
@ApiIgnore
public class ProjectController {
private final ProjectService projectService;
public ProjectController(ProjectService projectService) {
this.projectService = projectService;
}
@GetMapping("/dashboard")
public void getDashboard(ModelMap modelMap) {
modelMap.put("projects", projectService.findAllDto());
}
@GetMapping("/projects")
public void getProjects(ModelMap modelMap) {
modelMap.put("projects", projectService.findAllDto());
}
@GetMapping("/project")
public void getProject(ModelMap modelMap, @RequestParam(value = "id") Integer id) {
if (id != null && id > 0) {
modelMap.put("projectDto", projectService.findOneDto(id));
} else {
modelMap.put("projectDto", new ProjectDto());
}
}
@ModelAttribute("allStatuses")
public List<Project.ProjectStatus> getProjectStatuses() {
return projectService.getProjectStatuses();
}
}

@ -2,25 +2,61 @@ package ru.ulstu.project.model;
import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.Grant;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Project extends BaseEntity {
public enum ProjectStatus {
APPLICATION("Заявка"),
ON_COMPETITION("Отправлен на конкурс"),
SUCCESSFUL_PASSAGE("Успешное прохождение"),
IN_WORK("В работе"),
COMPLETED("Завершен"),
FAILED("Провалены сроки");
private String statusName;
ProjectStatus(String statusName) {
this.statusName = statusName;
}
public String getStatusName() {
return statusName;
}
}
@NotBlank
private String title;
@Enumerated(value = EnumType.STRING)
private ProjectStatus status = ProjectStatus.APPLICATION;
@NotNull
private String description;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "project_id")
private List<Deadline> deadlines = new ArrayList<>();
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "grant_id")
private Grant grant;
@NotNull
private String repository;
public String getTitle() {
return title;
}
@ -29,6 +65,38 @@ public class Project extends BaseEntity {
this.title = title;
}
public ProjectStatus getStatus() {
return status;
}
public void setStatus(ProjectStatus status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Grant getGrant() {
return grant;
}
public void setGrant(Grant grant) {
this.grant = grant;
}
public String getRepository() {
return repository;
}
public void setRepository(String repository) {
this.repository = repository;
}
public List<Deadline> getDeadlines() {
return deadlines;
}

@ -3,6 +3,7 @@ package ru.ulstu.project.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.GrantDto;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
@ -13,8 +14,11 @@ public class ProjectDto {
@NotEmpty
private String title;
private Project.ProjectStatus status;
private String description;
private List<Deadline> deadlines = new ArrayList<>();
private GrantDto grant;
private String repository;
public ProjectDto() {
}
@ -26,9 +30,17 @@ public class ProjectDto {
@JsonCreator
public ProjectDto(@JsonProperty("id") Integer id,
@JsonProperty("title") String title,
@JsonProperty("status") Project.ProjectStatus status,
@JsonProperty("description") String description,
@JsonProperty("grant") GrantDto grant,
@JsonProperty("repository") String repository,
@JsonProperty("deadlines") List<Deadline> deadlines) {
this.id = id;
this.title = title;
this.status = status;
this.description = description;
this.grant = grant;
this.repository = repository;
this.deadlines = deadlines;
}
@ -36,6 +48,10 @@ public class ProjectDto {
public ProjectDto(Project project) {
this.id = project.getId();
this.title = project.getTitle();
this.status = project.getStatus();
this.description = project.getDescription();
this.grant = project.getGrant() == null ? null : new GrantDto(project.getGrant());
this.repository = project.getRepository();
this.deadlines = project.getDeadlines();
}
@ -55,6 +71,38 @@ public class ProjectDto {
this.title = title;
}
public Project.ProjectStatus getStatus() {
return status;
}
public void setStatus(Project.ProjectStatus status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public GrantDto getGrant() {
return grant;
}
public void setGrant(GrantDto grant) {
this.grant = grant;
}
public String getRepository() {
return repository;
}
public void setRepository(String repository) {
this.repository = repository;
}
public List<Deadline> getDeadlines() {
return deadlines;
}

@ -2,17 +2,21 @@ package ru.ulstu.project.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;
import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.repository.ProjectRepository;
import java.util.Arrays;
import java.util.List;
import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
@Service
public class ProjectService {
private final static int MAX_DISPLAY_SIZE = 40;
private final ProjectRepository projectRepository;
private final DeadlineService deadlineService;
@ -27,6 +31,20 @@ public class ProjectService {
return projectRepository.findAll();
}
public List<ProjectDto> findAllDto() {
List<ProjectDto> projects = convert(findAll(), ProjectDto::new);
projects.forEach(projectDto -> projectDto.setTitle(StringUtils.abbreviate(projectDto.getTitle(), MAX_DISPLAY_SIZE)));
return projects;
}
public ProjectDto findOneDto(Integer id) {
return new ProjectDto(projectRepository.getOne(id));
}
public List<Project.ProjectStatus> getProjectStatuses() {
return Arrays.asList(Project.ProjectStatus.values());
}
@Transactional
public Project create(ProjectDto projectDto) {
Project newProject = copyFromDto(new Project(), projectDto);

@ -0,0 +1,16 @@
package ru.ulstu.students.controller;
import org.springframework.validation.Errors;
public class Navigation {
public static final String REDIRECT_TO = "redirect:%s";
public static final String TASKS_PAGE = "/students/tasks";
public static final String TASK_PAGE = "/students/task";
public static String hasErrors(Errors errors, String page) {
if (errors.hasErrors()) {
return page;
}
return null;
}
}

@ -0,0 +1,90 @@
package ru.ulstu.students.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.students.model.Task;
import ru.ulstu.students.model.TaskDto;
import ru.ulstu.students.service.TaskService;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.util.StringUtils.isEmpty;
import static ru.ulstu.students.controller.Navigation.REDIRECT_TO;
import static ru.ulstu.students.controller.Navigation.TASKS_PAGE;
import static ru.ulstu.students.controller.Navigation.TASK_PAGE;
@Controller()
@RequestMapping(value = "/students")
@ApiIgnore
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping("/tasks")
public void getTasks(ModelMap modelMap) {
modelMap.put("tasks", taskService.findAllDto());
}
@GetMapping("/dashboard")
public void getDashboard(ModelMap modelMap) {
modelMap.put("tasks", taskService.findAllDto());
}
@GetMapping("/task")
public void getTask(ModelMap modelMap, @RequestParam(value = "id") Integer id) {
if (id != null && id > 0) {
modelMap.put("taskDto", taskService.findOneDto(id));
} else {
modelMap.put("taskDto", new TaskDto());
}
}
@PostMapping(value = "/task", params = "save")
public String save(@Valid TaskDto taskDto, Errors errors) throws IOException {
filterEmptyDeadlines(taskDto);
if (taskDto.getDeadlines().isEmpty()) {
errors.rejectValue("deadlines", "errorCode", "Не может быть пустым");
}
if (errors.hasErrors()) {
return TASK_PAGE;
}
taskService.save(taskDto);
return String.format(REDIRECT_TO, TASKS_PAGE);
}
@PostMapping(value = "/task", params = "addDeadline")
public String addDeadline(@Valid TaskDto taskDto, Errors errors) {
filterEmptyDeadlines(taskDto);
if (errors.hasErrors()) {
return TASK_PAGE;
}
taskDto.getDeadlines().add(new Deadline());
return TASK_PAGE;
}
@ModelAttribute("allStatuses")
public List<Task.TaskStatus> getTaskStatuses() {
return taskService.getTaskStatuses();
}
private void filterEmptyDeadlines(TaskDto taskDto) {
taskDto.setDeadlines(taskDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
.collect(Collectors.toList()));
}
}

@ -0,0 +1,130 @@
package ru.ulstu.students.model;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.validator.constraints.NotBlank;
import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.tags.model.Tag;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
public class Task extends BaseEntity {
public enum TaskStatus {
IN_WORK("В работе"),
COMPLETED("Завершен"),
FAILED("Провалены сроки"),
LOADED_FROM_KIAS("Загружен автоматически");
private String statusName;
TaskStatus(String name) {
this.statusName = name;
}
public String getStatusName() {
return statusName;
}
}
@NotBlank
private String title;
private String description;
@Enumerated(value = EnumType.STRING)
private ru.ulstu.students.model.Task.TaskStatus status = TaskStatus.IN_WORK;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "task_id", unique = true)
@Fetch(FetchMode.SUBSELECT)
@OrderBy("date")
private List<Deadline> deadlines = new ArrayList<>();
@Column(name = "create_date")
@Temporal(TemporalType.TIMESTAMP)
private Date createDate = new Date();
@Column(name = "update_date")
@Temporal(TemporalType.TIMESTAMP)
private Date updateDate = new Date();
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "task_tags",
joinColumns = {@JoinColumn(name = "task_id")},
inverseJoinColumns = {@JoinColumn(name = "tag_id")})
private List<Tag> tags = new ArrayList<>();
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public TaskStatus getStatus() {
return status;
}
public void setStatus(TaskStatus status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Deadline> getDeadlines() {
return deadlines;
}
public void setDeadlines(List<Deadline> deadlines) {
this.deadlines = deadlines;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public List<Tag> getTags() {
return tags;
}
public void setTags(List<Tag> tags) {
this.tags = tags;
}
}

@ -0,0 +1,144 @@
package ru.ulstu.students.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.NotEmpty;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.tags.model.Tag;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class TaskDto {
private final static int MAX_TAGS_LENGTH = 50;
private Integer id;
@NotEmpty
private String title;
private String description;
private Task.TaskStatus status;
private List<Deadline> deadlines = new ArrayList<>();
private Date createDate;
private Date updateDate;
private Set<Integer> tagIds;
private List<Tag> tags;
public TaskDto() {
deadlines.add(new Deadline());
}
@JsonCreator
public TaskDto(@JsonProperty("id") Integer id,
@JsonProperty("title") String title,
@JsonProperty("description") String description,
@JsonProperty("createDate") Date createDate,
@JsonProperty("updateDate") Date updateDate,
@JsonProperty("status") Task.TaskStatus status,
@JsonProperty("deadlines") List<Deadline> deadlines,
@JsonProperty("tagIds") Set<Integer> tagIds,
@JsonProperty("tags") List<Tag> tags) {
this.id = id;
this.title = title;
this.status = status;
this.deadlines = deadlines;
this.createDate = createDate;
this.updateDate = updateDate;
this.description = description;
this.tags = tags;
}
public TaskDto(Task task) {
this.id = task.getId();
this.title = task.getTitle();
this.status = task.getStatus();
this.deadlines = task.getDeadlines();
this.createDate = task.getCreateDate();
this.updateDate = task.getUpdateDate();
this.description = task.getDescription();
this.tags = task.getTags();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Task.TaskStatus getStatus() {
return status;
}
public void setStatus(Task.TaskStatus status) {
this.status = status;
}
public List<Deadline> getDeadlines() {
return deadlines;
}
public void setDeadlines(List<Deadline> deadlines) {
this.deadlines = deadlines;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public Set<Integer> getTagIds() {
return tagIds;
}
public void setTagIds(Set<Integer> tagIds) {
this.tagIds = tagIds;
}
public List<Tag> getTags() {
return tags;
}
public void setTags(List<Tag> tags) {
this.tags = tags;
}
public String getTagsString() {
return StringUtils.abbreviate(tags
.stream()
.map(tag -> tag.getTagName())
.collect(Collectors.joining(", ")), MAX_TAGS_LENGTH);
}
}

@ -0,0 +1,7 @@
package ru.ulstu.students.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.students.model.Task;
public interface TaskRepository extends JpaRepository<Task, Integer> {
}

@ -0,0 +1,95 @@
package ru.ulstu.students.service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.students.model.Task;
import ru.ulstu.students.model.TaskDto;
import ru.ulstu.students.repository.TaskRepository;
import ru.ulstu.tags.service.TagService;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
import static ru.ulstu.students.model.Task.TaskStatus.IN_WORK;
@Service
public class TaskService {
private final static int MAX_DISPLAY_SIZE = 40;
private final TaskRepository taskRepository;
private final DeadlineService deadlineService;
private final TagService tagService;
public TaskService(TaskRepository grantRepository,
DeadlineService deadlineService, TagService tagService) {
this.taskRepository = grantRepository;
this.deadlineService = deadlineService;
this.tagService = tagService;
}
public List<Task> findAll() {
return taskRepository.findAll();
}
public List<TaskDto> findAllDto() {
List<TaskDto> tasks = convert(findAll(), TaskDto::new);
tasks.forEach(taskDto -> taskDto.setTitle(StringUtils.abbreviate(taskDto.getTitle(), MAX_DISPLAY_SIZE)));
return tasks;
}
public TaskDto findOneDto(Integer id) {
return new TaskDto(taskRepository.getOne(id));
}
@Transactional
public Integer create(TaskDto taskDto) throws IOException {
Task newTask = copyFromDto(new Task(), taskDto);
newTask = taskRepository.save(newTask);
return newTask.getId();
}
private Task copyFromDto(Task task, TaskDto taskDto) throws IOException {
task.setTitle(taskDto.getTitle());
task.setDescription(taskDto.getDescription());
task.setStatus(taskDto.getStatus() == null ? IN_WORK : taskDto.getStatus());
task.setDeadlines(deadlineService.saveOrCreate(taskDto.getDeadlines()));
task.setCreateDate(task.getCreateDate() == null ? new Date() : task.getCreateDate());
task.setUpdateDate(new Date());
task.getTags().clear();
task.setTags(tagService.saveOrCreate(taskDto.getTags()));
return task;
}
@Transactional
public Integer update(TaskDto taskDto) throws IOException {
Task task = taskRepository.getOne(taskDto.getId());
taskRepository.save(copyFromDto(task, taskDto));
return task.getId();
}
@Transactional
public void delete(Integer taskId) throws IOException {
Task task = taskRepository.getOne(taskId);
taskRepository.delete(task);
}
public void save(TaskDto taskDto) throws IOException {
if (isEmpty(taskDto.getId())) {
create(taskDto);
} else {
update(taskDto);
}
}
public List<Task.TaskStatus> getTaskStatuses() {
return Arrays.asList(Task.TaskStatus.values());
}
}

@ -0,0 +1,44 @@
package ru.ulstu.tags.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
import ru.ulstu.core.model.BaseEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.validation.constraints.Size;
@Entity
@Table(name = "tag")
public class Tag extends BaseEntity {
@NotEmpty
@Size(max = 50)
@Column(name = "tag_name")
private String tagName;
public Tag() {
}
@JsonCreator
public Tag(@JsonProperty("id") Integer id,
@JsonProperty("tag_name") String tagName) {
this.setId(id);
this.tagName = tagName;
}
public Tag(String name) {
this.tagName = name;
}
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
}

@ -0,0 +1,12 @@
package ru.ulstu.tags.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.ulstu.tags.model.Tag;
public interface TagRepository extends JpaRepository<Tag, Integer> {
@Query("SELECT t FROM Tag t WHERE (t.tagName = :tagName)")
Tag findByName(@Param("tagName") String tagName);
}

@ -0,0 +1,53 @@
package ru.ulstu.tags.service;
import org.springframework.stereotype.Service;
import ru.ulstu.tags.model.Tag;
import ru.ulstu.tags.repository.TagRepository;
import javax.transaction.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class TagService {
private final TagRepository tagRepository;
public TagService(TagRepository tagRepository) {
this.tagRepository = tagRepository;
}
public List<Tag> saveOrCreate(List<Tag> tags) {
return tags
.stream()
.map(tag -> {
if (tag.getId() != null) {
return getExistById(tag);
} else {
Tag existTag = isExistByName(tag.getTagName());
return existTag != null ? existTag : create(tag);
}
}).collect(Collectors.toList());
}
@Transactional
public Tag getExistById(Tag tag) {
return tagRepository.getOne(tag.getId());
}
@Transactional
public Tag isExistByName(String tagName) {
return tagRepository.findByName(tagName);
}
@Transactional
public Tag create(Tag tag) {
Tag newTag = new Tag();
newTag.setTagName(tag.getTagName());
newTag = tagRepository.save(newTag);
return newTag;
}
}

@ -124,7 +124,7 @@ public class EventService {
}
public void updatePaperDeadlines(Paper paper) {
eventRepository.delete(eventRepository.findAllByPaper(paper));
eventRepository.deleteAll(eventRepository.findAllByPaper(paper));
createFromPaper(paper);
}

@ -6,6 +6,8 @@ import ru.ulstu.core.model.BaseEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@ -24,6 +26,8 @@ import java.util.Set;
@Entity
@Table(name = "users")
public class User extends BaseEntity {
private final static String USER_ABBREVIATE_TEMPLATE = "%s %s%s";
@NotNull
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
@ -83,6 +87,28 @@ public class User extends BaseEntity {
@BatchSize(size = 20)
private Set<UserRole> roles;
@Column(name = "birth_date")
@Temporal(TemporalType.TIMESTAMP)
private Date birthDate;
public enum UserDegree {
CANDIDATE("Кандидат технических наук"),
DOCTOR("Доктор технических наук");
private String degreeName;
UserDegree(String degreeName) {
this.degreeName = degreeName;
}
public String getDegreeName() {
return degreeName;
}
}
@Enumerated(value = EnumType.STRING)
private UserDegree degree;
public User() {
roles = new HashSet<>();
activated = false;
@ -186,4 +212,27 @@ public class User extends BaseEntity {
public void setPatronymic(String patronymic) {
this.patronymic = patronymic;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public UserDegree getDegree() {
return degree;
}
public void setDegree(UserDegree degree) {
this.degree = degree;
}
public String getUserAbbreviate() {
return String.format(USER_ABBREVIATE_TEMPLATE,
lastName == null ? "" : lastName,
firstName == null ? "" : firstName.substring(0, 1) + ".",
patronymic == null ? "" : patronymic.substring(0, 1) + ".");
}
}

@ -15,6 +15,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
@ -70,6 +71,10 @@ public class UserDto implements OdinDto {
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
private String passwordConfirm;
private Date birthDate;
private User.UserDegree degree;
public UserDto() {
activated = false;
roles = new LinkedHashSet<>();
@ -86,6 +91,8 @@ public class UserDto implements OdinDto {
this.roles.addAll(user.getRoles().stream()
.map(UserRoleDto::new)
.collect(Collectors.toList()));
this.birthDate = user.getBirthDate();
this.degree = user.getDegree();
}
public Integer getId() {
@ -163,6 +170,22 @@ public class UserDto implements OdinDto {
return passwordConfirm;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public User.UserDegree getDegree() {
return degree;
}
public void setDegree(User.UserDegree degree) {
this.degree = degree;
}
@JsonIgnore
public boolean isPasswordsValid() {
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
@ -188,6 +211,8 @@ public class UserDto implements OdinDto {
", roles=" + roles +
", password='" + password + '\'' +
", passwordConfirm='" + passwordConfirm + '\'' +
", birthDate='" + birthDate + '\'' +
", degree='" + degree + '\'' +
'}';
}
}

@ -2,7 +2,13 @@ package ru.ulstu.user.model;
import ru.ulstu.core.model.BaseEntity;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import java.util.Date;

@ -2,6 +2,8 @@ package ru.ulstu.user.repository;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.ulstu.user.model.User;
import java.util.Date;
@ -25,4 +27,11 @@ public interface UserRepository extends JpaRepository<User, Integer> {
@EntityGraph(attributePaths = "roles")
User findOneWithRolesByLogin(String login);
@Query("SELECT u FROM User u " +
"WHERE (YEAR(CURRENT_DATE) - YEAR(u.birthDate) < 35 OR :hasAge = FALSE) " +
"AND (u.degree = 'CANDIDATE' OR :hasDegree = FALSE)" +
"ORDER BY u.lastName")
List<User> filterByAgeAndDegree(@Param("hasAge") boolean hasAge,
@Param("hasDegree") boolean hasDegree);
}

@ -325,4 +325,8 @@ public class UserService implements UserDetailsService {
}
return user;
}
public List<User> filterByAgeAndDegree(boolean hasDegree, boolean hasAge) {
return userRepository.filterByAgeAndDegree(hasDegree, hasAge);
}
}

@ -34,4 +34,5 @@ spring.liquibase.enabled=true
ng-tracker.base-url=http://127.0.0.1:8080
ng-tracker.undead-user-login=admin
ng-tracker.dev-mode=true
ng-tracker.use-https=false
ng-tracker.use-https=false
ng-tracker.check-run=false

@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="masha" id="20190323_000000-1">
<addColumn tableName="paper">
<column name="latex_text" type="varchar"/>
</addColumn>
<addColumn tableName="file">
<column name="is_latex_attach" type="boolean"/>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,26 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="tanya" id="20190402_000000-1">
<addColumn tableName="grants">
<column name="leader_id" type="integer"></column>
</addColumn>
<addForeignKeyConstraint baseTableName="grants" baseColumnNames="leader_id"
constraintName="fk_grants_leader_id" referencedTableName="users"
referencedColumnNames="id"/>
</changeSet>
<changeSet author="tanya" id="20190402_000000-2">
<createTable tableName="grants_authors">
<column name="grant_id" type="integer"/>
<column name="authors_id" type="integer"/>
</createTable>
<addForeignKeyConstraint baseTableName="grants_authors" baseColumnNames="grant_id"
constraintName="fk_grants_grants_authors" referencedTableName="grants"
referencedColumnNames="id"/>
<addForeignKeyConstraint baseTableName="grants_authors" baseColumnNames="authors_id"
constraintName="fk_user_grants_authors" referencedTableName="users"
referencedColumnNames="id"/>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,13 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="tanya" id="20190404_000000-1">
<addColumn tableName="users">
<column name="birth_date" type="timestamp"></column>
</addColumn>
<addColumn tableName="users">
<column name="degree" type="varchar(255)"></column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,17 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="nastya" id="20190410_000000-1">
<addColumn tableName="tag">
<column name="version" type="integer"/>
</addColumn>
<renameColumn tableName="tag" oldColumnName="tagname" newColumnName="tag_name"/>
</changeSet>
<changeSet author="nastya" id="20190410_000000-2">
<addColumn tableName="task">
<column name="version" type="integer"/>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="vova" id="20190417_000000-1">
<addColumn tableName="users_conference">
<column name="id" type="integer"></column>
<column name="version" type="integer"></column>
</addColumn>
<addPrimaryKey columnNames="id" constraintName="pk_users_conference" tableName="users_conference"/>
<modifyDataType tableName="users_conference" columnName="participation" newDataType="varchar(255)"/>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,22 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="anton" id="20190418_000000-1">
<addColumn tableName="project">
<column name="status" type="varchar(255)"/>
</addColumn>
<addColumn tableName="project">
<column name="description" type="varchar(255)"/>
</addColumn>
<addColumn tableName="project">
<column name="grant_id" type="integer"/>
</addColumn>
<addForeignKeyConstraint baseTableName="project" baseColumnNames="grant_id"
constraintName="fk_project_grant_id" referencedTableName="grants"
referencedColumnNames="id"/>
<addColumn tableName="project">
<column name="repository" type="varchar(255)"/>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,15 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="masha" id="20190421_000000-1">
<addColumn tableName="paper">
<column name="type" type="varchar(255)"/>
</addColumn>
</changeSet>
<changeSet author="masha" id="20190421_000000-2">
<update tableName="paper">
<column name="type" value="OTHER"/>
</update>
</changeSet>
</databaseChangeLog>

@ -23,5 +23,13 @@
<include file="db/changelog-20190327_000000-schema.xml"/>
<include file="db/changelog-20190331_000000-schema.xml"/>
<include file="db/changelog-20190331_000010-schema.xml"/>
<include file="db/changelog-20190410_000000-schema.xml"/>
<include file="db/changelog-20190417_000000-schema.xml"/>
<include file="db/changelog-20190418_000000-schema.xml"/>
<include file="db/changelog-20190323_000001-schema.xml"/>
<include file="db/common/changelog-20190312_130000-schema.xml"/>
<include file="db/changelog-20190402_000000-schema.xml"/>
<include file="db/changelog-20190404_000000-schema.xml"/>
<include file="db/changelog-20190421_000000-schema.xml"/>
<include file="db/common/changelog-20190423_100000-schema.xml"/>
</databaseChangeLog>

@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="orion" id="20190423_100000-1">
<addColumn tableName="hibernate_sequences">
<column name="next_val" type="bigint"/>
</addColumn>
</changeSet>
</databaseChangeLog>

File diff suppressed because it is too large Load Diff

@ -2,7 +2,22 @@ body {
min-width: 400px;
}
.conference-row .col:hover {
background-color: #f3f3f3;
border-radius: .25rem;
}
.filter-option-inner-inner {
color: white;
}
.filter .dropdown {
margin-bottom: 10px;
}
.conference-row .col .text-decoration {
text-decoration: none;
}
.form-group textarea {
@ -19,6 +34,7 @@ body {
.deadline {
margin: 0;
height: 40px;
min-height: 40px;
}
.deadline-text {
@ -33,8 +49,46 @@ body {
.member {
margin: 0;
height: 40px;
max-height: 40px;
}
.member select {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
border: none;
outline: none;
padding: 0.5rem 1.75em 0.5rem 0.5em;
display: inline-block;
background: transparent url("https://cdn3.iconfinder.com/data/icons/faticons/32/arrow-down-01-16.png") no-repeat right 7px center;
}
.member select:nth-child(4) {
border-right: 1px solid #ced4da;
}
.member-name {
padding: .75rem 1.25rem;
cursor: default;
outline: none;
border: none;
border-right: 1px solid #ced4da;
}
#take-part[disabled=disabled] {
background-color: #ced4da;
border-color: #c2c5c7;
}
#take-part[disabled=disabled]:hover {
background-color: #737475 !important;
border-color: #5d5e5f !important;
}
.paper-list {
height: 200px;
padding: 0px;
@ -43,6 +97,8 @@ body {
.paper {
margin: 0;
min-height: 40px;
height: 40px;
}
.paper-name {
@ -63,6 +119,9 @@ body {
.icon-delete {
background-color: #f44;
background-image: url(/img/conference/delete.png);
background-repeat: round;
color: transparent !important;
}
.icon-delete:hover {
@ -95,13 +154,14 @@ body {
float: right;
}
@media (max-width: 1199px) {
@media (max-width: 1199px) and (min-width: 768px) {
.paper-control {
display: block!important;
}
}
@media (max-width: 991px) {
.dates-panel {
display: block!important;
}

@ -0,0 +1,12 @@
.div-deadline-date {
padding-right: 0px;
}
.form-deadline-date {
padding-right: 3px;
}
.div-deadline-description {
padding-left: 5px;
padding-right: 5px;
}

@ -1,5 +1,9 @@
#files-list .row > div:nth-child(6) {
#files-list .row > div:nth-child(7) {
display: flex;
justify-content: center;
flex-direction: column;
}
.nav-tabs {
margin-bottom: 20px;
}

@ -1,24 +1,65 @@
.bootstrap-tagsinput{
.tags-container {
width: 100%;
padding: .375rem .75rem;
background-color: #fff;
border: 1px solid #ccc;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
display: inline-block;
color: #555;
vertical-align: middle;
border-radius: 4px;
max-width: 100%;
line-height: 22px;
cursor: text;
}
.input-tag-name {
border: none;
box-shadow: none;
outline: none;
background-color: transparent;
padding: 0 6px;
margin: 0;
width: auto;
max-width: inherit;
}
.bootstrap-tagsinput .label{
.tag {
display: inline-block;
padding: .2em .6em .3em;
background-color: orange;
border-radius: .25em;
margin-right: 4px;
margin-bottom: 4px;
font-size: 75%;
font-weight: 700;
line-height: 1.5;
color: #fff;
}
.tag-name span[data-role="remove"] {
margin-left: 8px;
cursor: pointer;
}
display: inline;
padding: .2em .6em .3em;
font-size: 75%;
font-weight: 700;
line-height: 2.5;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
.tag-name span[data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
.bootstrap-tagsinput .label-info{
.tag-name input[type="text"] {
background: transparent;
border: none;
display: inline-flex;
font-size: 100%;
font-weight: 700;
line-height: 1.5;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
outline: none;
cursor: default;
background-color: orange;
}

@ -1,27 +1,29 @@
$(document).ready(function () {
$('.data-href-js').click( function() {
window.location = $(this).attr('data-href');
});
$('.circle').parent().click( function() {
$(this).children('.circle').toggleClass('circle-active');
});
$('.checkbox-js').parent().click( function() {
$(this).children('.checkbox').toggleClass('selected');
});
$('#select-all-js').click( function() {
$(this).toggleClass('selected');
$('a[data-confirm]').click(function (ev) {
var href = $(this).attr('href');
if (!$('#dataConfirmModal').length) {
$('#modalDelete').append('<div class="modal fade" id="dataConfirmModal" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true"\n' +
' >\n' +
' <div class="modal-dialog modal-sm">\n' +
' <div class="modal-content">\n' +
' <div class="modal-header">\n' +
' <h8 class="modal-title" id="myModalLabel">Удалить конференцию?</h8>\n' +
' <button type="button" class="close" data-dismiss="modal" aria-label="Закрыть"><span\n' +
' aria-hidden="true">&times;</span></button>\n' +
' </div>\n' +
var childNodes = $('.conference-item .form-check .checkbox')
.each(function(i, elem) {
if ($(this).hasClass('selected') && !$('#select-all-js').hasClass('selected')) {
$(this).toggleClass('selected');
} else if (!$(this).hasClass('selected') && $('#select-all-js').hasClass('selected')){
$(this).toggleClass('selected');
}
});
' <div class="modal-footer">\n' +
' <a class="btn btn-primary" id="dataConfirmOK">Да</a>' +
' <button class="btn primary" data-dismiss="modal" aria-hidden="true">Нет</button>' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' </div>');
}
$('#dataConfirmModal').find('#myModalLabel').text($(this).attr('data-confirm'));
$('#dataConfirmOK').attr('href', href);
$('#dataConfirmModal').modal({show: true});
return false;
});
});

@ -3,6 +3,7 @@
var urlFileUpload = "/api/1.0/files/uploadTmpFile";
var urlFileDownload = "/api/1.0/files/download/";
var urlPdfGenerating = "/papers/generatePdf";
var urlFileDownloadTmp = "/api/1.0/files/download-tmp/";
/* exported MessageTypesEnum */

@ -1,5 +1,75 @@
/*<![CDATA[*/
$(document).ready(function () {
$("#tags .tag .tag-name input[type=text]").each(function () {
$(this).attr("size", $(this).val().length)
});
$("#task-form").keydown(function (event) {
if (event.keyCode == 13) {
event.preventDefault();
return false;
}
});
function removeTag() {
$(this).parent().parent().remove();
}
$("#input-tag").keyup(function (event) {
if (event.keyCode == 13 || event.keyCode == 188) {
var tagNumber = $("#tags .tag").length;
var tagName = $.trim($(this).val());
var addTag = true;
// проверка, добавлен ли этот тег
$("#tags .tag .tag-name input[type=text]").each(function () {
if (tagName === $(this).val()) {
addTag = false;
return;
}
});
// если тег не добавлен
if (addTag) {
// контейнер тега
var newTagRow = $("<div/>")
.attr("id", 'tags' + tagNumber)
.addClass("tag");
// контейнер id
var idInput = $("<input/>")
.attr("type", "hidden")
.attr("id", "tags" + tagNumber + ".id")
.attr("name", "tags[" + tagNumber + "].id")
.attr("value", '');
// контейнер текста
var conDiv = $("<div/>")
.addClass("tag-name");
// текст тега
var nameInput = $("<input/>")
.attr("type", "text")
.attr("id", "tags" + tagNumber + ".tagName")
.attr("name", "tags[" + tagNumber + "].tagName")
.attr("value", tagName)
.attr("readonly", "true")
.attr("size", tagName.length);
// кнопка удалить тег
var removeSpan = $("<span/>")
.attr("data-role", "remove")
.bind("click", removeTag);
conDiv.append(nameInput);
conDiv.append(removeSpan);
newTagRow.append(idInput);
newTagRow.append(conDiv);
$(this).before(newTagRow);
}
$(this).val('');
}
});
$("span[data-role=remove]").click(removeTag);
$(".task-row").mouseenter(function (event) {
var taskRow = $(event.target).closest(".task-row");
$(taskRow).css("background-color", "#f8f9fa");

@ -18,47 +18,59 @@
<hr/>
<div class="row">
<div class="col-lg-12">
<form id="conference-form" method="post">
<form id="conference-form" method="post"
th:action="@{'/conferences/conference?id='+ *{id == null ? '' : id} + ''}"
th:object="${conferenceDto}">
<div class="row">
<div class="col-md-7 col-sm-12">
<input type="hidden" name="id"/>
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label for="title">Название:</label>
<input class="form-control" id="title" type="text"
<input class="form-control" th:field="*{title}" id="title" type="text"
placeholder="Название конференции"/>
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label for="url">URL:</label>
<input class="form-control" id="url" type="text"
placeholder="URL адрес"/>
<input class="form-control" th:field="*{url}" id="url" type="text"
placeholder="http://"/>
</div>
<div class="form-group">
<label for="description">Описание:</label>
<textarea class="form-control" rows="8" id="description">
<textarea class="form-control" rows="8" th:field="*{description}" id="description">
</textarea>
</div>
<div class="form-group">
<label for="deadlines">Дедлайны:</label>
<div class="deadline-list form-control list-group" id="deadlines">
<div class="deadline d-flex p-0 list-group-item list-group-horizontal">
<input type="hidden" th:field="*{removedDeadlineIds}"/>
<div class="deadline d-flex p-0 list-group-item list-group-horizontal"
th:each="deadline, rowStat : *{deadlines}">
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
<input class="deadline-text col-md list-group-item" type="text"
placeholder="Текст дедлайна"/>
<input class="list-group-item" type="date"/>
<img class="icon icon-delete grey-border" src="/img/conference/delete.png"
alt="Удалить"/>
placeholder="Описание"
th:field="*{deadlines[__${rowStat.index}__].description}"/>
<input class="list-group-item" type="date" name="deadline"
th:field="*{deadlines[__${rowStat.index}__].date}"/>
<input type="submit" class="icon icon-delete grey-border"
alt="Удалить" name="removeDeadline" th:value="${rowStat.index}"/>
</div>
</div>
</div>
<p th:if="${#fields.hasErrors('deadlines')}" th:errors="*{deadlines}"
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
<div class="form-group d-flex justify-content-end">
<input type="submit" id="add-deadline" name="add-deadline"
<input type="submit" id="addDeadline" name="addDeadline"
class="btn btn-primary"
value="Добавить дедлайн"/>
</div>
@ -70,11 +82,13 @@
<div class="row" id="dates">
<div class="d-flex col justify-content-between dates-panel">
<div class="date">
<input class="grey-border form-control" type="date" id="date-begin"/>
<input class="grey-border form-control" type="date"
th:field="*{beginDate}" id="begin-date"/>
</div>
<div class="date">
<input class="grey-border form-control" type="date" id="date-end"/>
<input class="grey-border form-control" type="date"
th:field="*{endDate}" id="end-date"/>
</div>
</div>
</div>
@ -83,60 +97,64 @@
<div class="form-group">
<label for="members">Участники:</label>
<div class="member-list form-control list-group" id="members">
<div class="member d-flex list-group-item justify-content-between p-1">
<div class="member-name">
Пользователь 1
</div>
<div class="member-participation">
очная
</div>
<div class="member-deposit">
статья
</div>
</div>
<div class="member d-flex list-group-item justify-content-between p-1">
<div class="member-name">
Пользователь 1
</div>
<div class="member-participation">
очная
</div>
<div class="member-deposit">
доклад
</div>
<div class="member d-flex list-group-item p-0"
th:each="user, rowStat : *{users}">
<input type="hidden" th:field="*{users[__${rowStat.index}__].id}"/>
<input type="hidden" th:field="*{users[__${rowStat.index}__].user}"/>
<input class="member-name w-100" readonly="true"
th:field="*{users[__${rowStat.index}__].user.lastName}"/>
<select class="member-participation w-auto"
th:field="*{users[__${rowStat.index}__].participation}">
<option th:each="participation : ${allParticipation}"
th:value="${participation}"
th:text="${participation.participation}">Participation
</option>
</select>
<select class="member-deposit w-auto"
th:field="*{users[__${rowStat.index}__].deposit}">
<option th:each="deposit : ${allDeposit}" th:value="${deposit}"
th:text="${deposit.deposit}">Deposit
</option>
</select>
</div>
</div>
</div>
<div class="form-group d-flex justify-content-end">
<button id="take-part" class="btn btn-primary"
type="button">
Принять участие
</button>
<input type="hidden" th:value="*{disabledTakePart}" th:name="disabledTakePart"/>
<input id="take-part" class="btn btn-primary"
type="submit" name="takePart" value="Принять участие"
th:disabled="*{disabledTakePart}"/>
</div>
<div class="form-group">
<label for="papers">Статьи:</label>
<div class="paper-list form-control list-group" id="papers">
<div class="paper d-flex list-group-item p-0">
<a class="paper-name" href="/papers/papers">
<span>
<input th:type="hidden" th:field="*{papers}"/>
<div class="paper d-flex list-group-item p-0"
th:each="paper, rowStat : *{papers}">
<a class="paper-name"
th:href="@{'/papers/paper?id=' + *{papers[__${rowStat.index}__].id} + ''}">
<span th:text="*{papers[__${rowStat.index}__].title}">
Имя статьи
</span>
<img class="icon-paper" src="/img/conference/paper.png"/>
</a>
<img class="icon grey-border icon-delete" src="/img/conference/delete.png"
alt="Удалить"/>
<input type="submit" class="icon icon-delete grey-border"
alt="Удалить" name="removePaper" th:value="${rowStat.index}"/>
</div>
</div>
</div>
<div class="paper-control form-group d-flex justify-content-end">
<button id="attach-paper" class="btn btn-primary"
type="button">
Прикрепить статью
</button>
<input th:type="hidden" th:field="*{notSelectedPapers}"/>
<select class="selectpicker form-control" multiple="true" data-live-search="true"
title="Прикрепить статью" data-style="btn-primary" data-size="5"
th:field="*{paperIds}">
<option th:each="paper: *{notSelectedPapers}" th:value="${paper.id}"
th:text="${paper.title}">Status
</option>
</select>
<button id="add-paper" class="btn btn-primary"
type="button">
Добавить статью

@ -3,11 +3,12 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}" xmlns:th="">
<head>
<link rel="stylesheet" type="text/css" href="../css/conference.css"/>
</head>
<body>
<div layout:fragment="content">
<form id="conferences-form" method="post">
<form id="conferences-form" method="post" th:action="@{'/conferences/conferences'}">
<section id="conferences">
<div class="container">
<div class="row" id="conference-list">
@ -19,21 +20,26 @@
<hr/>
<div class="row">
<div class="col-md-9 col-sm-12">
<th:block th:each="conference : ${filteredConferences.conferences}">
<div th:replace="conferences/fragments/confLineFragment :: confLine(conference=${conference})"/>
</th:block>
</div>
<div class="col-md-3 col-sm-12">
<div class="filter">
<h5>Фильтр:</h5>
<select class="form-control" id="author"
onchange="this.form.submit();">
<option value="">Все авторы</option>
<option>lastName
<select class="selectpicker form-control" id="user"
th:field="${filteredConferences.filterUserId}"
onchange="this.form.submit();" data-style="btn-primary" data-size="5">
<option value="">Все участники</option>
<option th:each="user: ${allUsers}" th:value="${user.id}"
th:text="${user.lastName}">lastName
</option>
</select>
<select class="form-control" id="year"
onchange="this.form.submit();">
<select class="selectpicker form-control" id="year" th:field="${filteredConferences.year}"
onchange="this.form.submit();" data-style="btn-primary" data-size="5">
<option value="">Все годы</option>
<option>year
<option th:each="year: ${allYears}" th:value="${year}"
th:text="${year}">year
</option>
</select>
</div>
@ -44,7 +50,7 @@
<div id="modalDelete"/>
</form>
<script></script>
<script src="../js/conference.js"></script>
</div>
</body>

@ -16,11 +16,10 @@
</div>
</div>
<hr/>
<div class="row">
<div class="col-lg-12">
</div>
<div class="row justify-content-center" id="dashboard">
<th:block th:each="conference : ${conferences}">
<div th:replace="conferences/fragments/confDashboardFragment :: confDashboard(conference=${conference})"/>
</th:block>
</div>
</div>
</section>

@ -10,8 +10,14 @@
</div>
<div class="col col-10 text-right">
<h7 class="service-heading"> title</h7>
<p class="text-muted"></p>
<p th:if="${conference.url!=null and conference.url!=''}"><a target="_blank" th:href="${conference.url}"><i
class="fa fa-external-link fa-1x"
aria-hidden="true"></i></a></p>
<p th:unless="${conference.url!=null and conference.url!=''}"><i class="fa fa-fw fa-2x"
aria-hidden="true"></i></p>
<a th:href="'conference?id='+${conference.id}">
<h7 class="service-heading" th:text="${conference.title}"> title</h7>
</a>
</div>
</div>
</div>

@ -4,15 +4,15 @@
<meta charset="UTF-8"/>
</head>
<body>
<div th:fragment="confLine (conference)" class="row text-left paper-row" style="background-color: white;">
<div class="col">
<a href="/conference">
<span class="h6"></span>
<span class="text-muted"></span>
<div th:fragment="confLine (conference)" class="row text-left conference-row h3" style="background-color: white;">
<div class="col d-flex justify-content-between">
<a th:href="@{'conference?id='+${conference.id}}" class="w-100 text-decoration">
<span class="h5" th:text="${conference.title}"/>
<span class="text-muted h6 float-right m-2" th:text="${conference.datesString}"/>
</a>
<input class="id-class" type="hidden"/>
<a class="remove-paper pull-right d-none"
data-confirm="Удалить статью?">
<input class="id-class" type="hidden" th:value="${conference.id}"/>
<a class="remove-paper pull-right m-auto" th:href="@{'/conferences/delete/'+${conference.id}}"
data-confirm="Удалить конференцию?">
<i class="fa fa-trash" aria-hidden="true"></i>
</a>
</div>

@ -13,7 +13,7 @@
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./actual" class="btn btn-light toolbar-button">
<a href="./dashboard" class="btn btn-light toolbar-button">
<i class="fa fa-newspaper-o" aria-hidden="true"></i>
Актуальное</a>
</div>
@ -21,7 +21,7 @@
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./conference?id=0" class="btn btn-light toolbar-button">
<i class="fa fa-plus-circle" aria-hidden="true"></i>
Добавить конференцию</a>
Новая конференцию</a>
</div>
</div>

@ -10,7 +10,9 @@
<span th:replace="grants/fragments/grantStatusFragment :: grantStatus(grantStatus=${grant.status})"/>
</div>
<div class="col col-10 text-right">
<h7 class="service-heading" th:text="${grant.title}"> title</h7>
<a th:href="'grant?id='+${grant.id}">
<h7 class="service-heading" th:text="${grant.title}"> title</h7>
</a>
<p class="text-muted" th:text="${grant.status.statusName}"> status</p>
</div>
</div>

@ -9,7 +9,7 @@
<span th:replace="grants/fragments/grantStatusFragment :: grantStatus(grantStatus=${grant.status})"/>
<a th:href="@{'grant?id='+${grant.id}}">
<span class="h6" th:text="${grant.title}"/>
<span class="text-muted" th:text="${grant.comment}"/>
<span class="text-muted" th:text="${grant.authorsString}"/>
</a>
<input class="id-class" type="hidden" th:value="${grant.id}"/>
<a class="remove-paper pull-right d-none" th:href="@{'/grants/delete/'+${grant.id}}"

@ -4,7 +4,7 @@
<meta charset="UTF-8"/>
</head>
<body>
<span th:fragment="paperStatus (paperStatus)" class="fa-stack fa-1x">
<span th:fragment="grantStatus (grantStatus)" class="fa-stack fa-1x">
<th:block th:switch="${grantStatus.name()}">
<div th:case="'APPLICATION'">
<i class="fa fa-circle fa-stack-2x text-draft"></i>

@ -3,13 +3,12 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head>
<link rel="stylesheet" href="../css/grant.css"/>
</head>
<body>
<div class="container" layout:fragment="content">
<section id="paper">
<section id="grant">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
@ -17,6 +16,7 @@
<div th:replace="grants/fragments/grantNavigationFragment"/>
</div>
</div>
<hr/>
<div class="row">
<div class="col-lg-12">
<form id="grant-form" method="post" th:action="@{'/grants/grant?id='+ *{id == null ? '' : id} + ''}"
@ -33,7 +33,6 @@
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label for="status">Статус:</label>
<select class="form-control" th:field="*{status}" id="status">
@ -51,11 +50,11 @@
<label>Дедлайны показателей:</label>
<div class="row" th:each="deadline, rowStat : *{deadlines}">
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
<div class="col-6">
<input type="date" class="form-control" name="deadline"
<div class="col-6 div-deadline-date">
<input type="date" class="form-control form-deadline-date" name="deadline"
th:field="*{deadlines[__${rowStat.index}__].date}"/>
</div>
<div class="col-4">
<div class="col-4 div-deadline-description">
<input class="form-control" type="text" placeholder="Описание"
th:field="*{deadlines[__${rowStat.index}__].description}"/>
</div>
@ -71,31 +70,101 @@
class="alert alert-danger">Incorrect title</p>
</div>
<div class="form-group">
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary" value="Добавить
дедлайн"/>
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
value="Добавить дедлайн"/>
</div>
<div class="form-group">
<label for="loader">Загрузить заявку:</label>
<div id="loader">
</div>
</div>
</div>
<div class="col-md-6 col-sm-12">
<label data-toggle="collapse"
href="#collapse-filter"
aria-expanded="false"
aria-controls="collapse-filter">Фильтр рабочей группы
</label>
<div th:class="${grantDto.wasLeader || grantDto.hasAge || grantDto.hasDegree} ?
'form-check' : 'form-check collapse'" id="collapse-filter">
<div class="row">
<div class="col">
<input class="form-check-input" type="checkbox" id="f1"
th:field="*{wasLeader}" th:onclick="|$('#filter').click();|"/>
<label class="form-check-label" for="f1">Был руководителем проекта</label>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id="f2"
th:field="*{hasAge}" th:onclick="|$('#filter').click();|"/>
<label class="form-check-label" for="f2">Младше 35 лет</label>
</div>
</div>
<div class="row">
<div class="col">
<input class="form-check-input" type="checkbox" id="f3"
th:field="*{hasDegree}" th:onclick="|$('#filter').click();|"/>
<label class="form-check-label" for="f3">епень к.т.н.</label>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id="f4"/>
<label class="form-check-label" for="f4">Более 3-х публикаций в
scopus</label>
</div>
</div>
<div class="row">
<div class="col">
<input class="form-check-input" type="checkbox" id="f5"/>
<label class="form-check-label" for="f5">Наличие ВАК статей</label> <br/>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id="f6"/>
<label class="form-check-label" for="f6">Наименьшая загруженность</label>
</div>
</div>
<input type="submit" hidden="hidden" id="filter" name="filterUsers"
value="Применить фильтр"/>
<hr/>
</div>
<div class="form-group">
<label>Руководитель проекта:</label>
<select class="form-control" th:field="*{leaderId}" id="leaderId"
onchange="updateAuthors();">
<option selected="selected" hidden="hidden" th:value="-1">-- Выберите
руководителя --
</option>
<option th:each="leader : ${allAuthors}" th:value="${leader.id}"
th:text="${leader.lastName}"> Руководитель
</option>
</select>
<p th:if="${#fields.hasErrors('leaderId')}" th:errors="*{leaderId}"
class="alert alert-danger">Choose leader</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label>Участники гранта:</label>
<select class="selectpicker form-control" multiple="true"
title="-- Выберите участников --" id="authors"
th:field="*{authorIds}">
<option th:each="author : ${allAuthors}" th:value="${author.id}"
th:text="${author.lastName}"> Участник
</option>
</select>
</div>
<div class="form-group">
<label>Список статей:</label>
<p><a href="./#" class="btn btn-primary" ><i class="fa fa-plus-circle" aria-hidden="true">
<p><a href="./#" class="btn btn-primary"><i class="fa fa-plus-circle"
aria-hidden="true">
</i> Добавить статью</a></p>
</div>
<div class="form-group">
<div th:if="*{project} == null">
<input type="submit" name="createProject" class="btn btn-primary"
<input type="submit" name="createProject" class="btn btn-primary"
value="Добавить проект"/>
</div>
<input type = "hidden" th:field="*{project.id}"/>
</div>
</div>
<div class="clearfix"></div>
<div class="col-lg-12">
<div class="form-group">
@ -131,6 +200,17 @@
$('.selectpicker').selectpicker();
});
/*]]>*/
</script>
<script type="text/javascript">
function updateAuthors() {
$("#authors").val('default');
$("#authors").selectpicker("refresh");
$("#authors").children('option:disabled').prop('disabled', false);
var lid = $("#leaderId option:selected").val();
$("#authors [value='" + lid + "']").attr("disabled", "disabled");
}
</script>
</div>
</body>

@ -10,12 +10,6 @@
<!-- Portfolio Grid -->
<section class="bg-light" id="portfolio">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Work hard</h2>
<h3 class="section-subheading text-muted">sometimes</h3>
</div>
</div>
<div class="row">
<div class="col-md-4 col-sm-6 portfolio-item">
<a class="portfolio-link" href="./papers/papers">
@ -46,7 +40,7 @@
</div>
</div>
<div class="col-md-4 col-sm-6 portfolio-item">
<a class="portfolio-link" data-toggle="modal" href="/projects">
<a class="portfolio-link" href="./projects/dashboard">
<div class="portfolio-hover">
<div class="portfolio-hover-content">
<i class="fa fa-arrow-right fa-3x"></i>
@ -88,7 +82,7 @@
</div>
</div>
<div class="col-md-4 col-sm-6 portfolio-item">
<a class="portfolio-link" href="./students/tasks">
<a class="portfolio-link" href="./students/tasks">
<div class="portfolio-hover">
<div class="portfolio-hover-content">
<i class="fa fa-arrow-right fa-3x"></i>

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
</head>
<body>
<body>
<div th:fragment="filesList (isLatexAttach)" th:remove="tag">
<th:block th:each="file, rowStat : *{files}">
<span th:if="${(!isLatexAttach and file.isLatexAttach == null) or file.isLatexAttach == isLatexAttach}"
th:remove="tag">
<div class="row" th:id="|files${rowStat.index}|"
th:style="${file.deleted} ? 'display: none;' :''">
<input type="hidden" th:field="*{files[__${rowStat.index}__].id}"/>
<input type="hidden"
th:field="*{files[__${rowStat.index}__].deleted}"/>
<input type="hidden"
th:field="*{files[__${rowStat.index}__].name}"/>
<input type="hidden"
th:field="*{files[__${rowStat.index}__].tmpFileName}"/>
<input type="hidden"
th:field="*{files[__${rowStat.index}__].isLatexAttach}"/>
<div class="col-2">
<a class="btn btn-danger float-right"
th:onclick="|$('#files${rowStat.index}\\.deleted').val('true'); $('#files${rowStat.index}').hide(); |">
<span aria-hidden="true"><i class="fa fa-times"/></span>
</a>
</div>
<div class="col-10">
<a th:onclick="${file.id==null} ? 'downloadFile('+${file.tmpFileName}+',null,\''+${file.name}+'\')':
'downloadFile(null,'+${file.id}+',\''+${file.name}+'\')' "
href="javascript:void(0)"
th:text="*{files[__${rowStat.index}__].name}">
</a>
</div>
</div>
</span>
</th:block>
</div>
</body>
</body>
</html>

@ -20,35 +20,56 @@
<hr/>
<div class="row">
<div class="col-lg-12">
<form id="paper-form" method="post" th:action="@{'/papers/paper?id='+ *{id == null ? '' : id} + ''}"
<form name="paperform" id="paper-form" method="post"
th:action="@{'/papers/paper?id='+ *{id == null ? '' : id} + ''}"
th:object="${paperDto}">
<div class="row">
<div class="col-md-7 col-sm-12">
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label for="title">Название:</label>
<input class="form-control" id="title" type="text"
placeholder="Название статьи"
th:field="*{title}"/>
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
</div>
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
<a class="nav-item nav-link active" id="nav-main-tab" data-toggle="tab"
href="#nav-main" role="tab" aria-controls="nav-main" aria-selected="true">Статья</a>
<a class="nav-item nav-link" id="nav-latex-tab" data-toggle="tab"
href="#nav-latex" role="tab" aria-controls="nav-latex" aria-selected="false">Latex</a>
</div>
</nav>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-main" role="tabpanel"
aria-labelledby="nav-main-tab">
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label for="title">Название:</label>
<input class="form-control" id="title" type="text"
placeholder="Название статьи"
th:field="*{title}"/>
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label for="status">Статус:</label>
<select class="form-control" th:field="*{status}" id="status">
<option th:each="status : ${allStatuses}" th:value="${status}"
th:text="${status.statusName}">Status
</option>
</select>
</div>
<div class="form-group">
<label for="type">Тип статьи:</label>
<select class="form-control" th:field="*{type}" id="type">
<option th:each="type : ${allTypes}" th:value="${type}"
th:text="${type.typeName}">Type
</option>
</select>
</div>
<div class="form-group">
<label for="comment">Комментарий:</label>
<textarea class="form-control" rows="5" id="comment"
th:field="*{comment}"></textarea>
</div>
<div class="form-group">
<label for="status">Статус:</label>
<select class="form-control" th:field="*{status}" id="status">
<option th:each="status : ${allStatuses}" th:value="${status}"
th:text="${status.statusName}">Status
</option>
</select>
</div>
<div class="form-group">
<label for="comment">Комментарий:</label>
<textarea class="form-control" rows="5" id="comment"
th:field="*{comment}"></textarea>
</div>
<div class="form-group">
<label for="title">Ссылка на сайт конференции:</label>
@ -99,42 +120,52 @@
class="alert alert-danger">Incorrect title</p>
</div>
<div class="form-group" id="files-list">
<label>Файлы:</label>
<th:block th:each="file, rowStat : *{files}">
<div class="row" th:id="|files${rowStat.index}|"
th:style="${file.deleted} ? 'display: none;' :''">
<input type="hidden" th:field="*{files[__${rowStat.index}__].id}"/>
<input type="hidden" th:field="*{files[__${rowStat.index}__].deleted}"/>
<input type="hidden" th:field="*{files[__${rowStat.index}__].name}"/>
<input type="hidden" th:field="*{files[__${rowStat.index}__].tmpFileName}"/>
<div class="col-2">
<a class="btn btn-danger float-right"
th:onclick="|$('#files${rowStat.index}\\.deleted').val('true'); $('#files${rowStat.index}').hide(); |">
<span aria-hidden="true"><i class="fa fa-times"/></span>
</a>
</div>
<div class="col-10">
<a th:onclick="${file.id==null} ? 'downloadFile('+${file.tmpFileName}+',null,\''+${file.name}+'\')':
'downloadFile(null,'+${file.id}+',\''+${file.name}+'\')' "
href="javascript:void(0)"
th:text="*{files[__${rowStat.index}__].name}">
</a>
</div>
<div class="form-group files-list" id="files-list">
<label>Файлы:</label>
<div th:replace="papers/fragments/paperFilesListFragment :: filesList(isLatexAttach = ${false})"/>
</div>
</th:block>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="locked"
th:field="*{locked}"/>
<label class="form-check-label" for="locked">Заблокирована</label>
</div>
<div class="form-group">
<label for="loader">Загрузить статью:</label>
<div id="loader">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="locked"
th:field="*{locked}"/>
<label class="form-check-label" for="locked">Заблокирована</label>
</div>
<div class="form-group">
<label for="loader">Загрузить статью:</label>
<div id="loader">
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-latex" role="tabpanel"
aria-labelledby="nav-profile-tab">
<div class="form-group">
<label for="latex-text">Latex текст:</label>
<textarea class="form-control" id="latex-text" type="text" rows="10"
placeholder="Latex.."
th:field="*{latexText}"/>
</div>
<div class="form-group files-list" id="latex-files-list">
<label>Файлы:</label>
<div th:replace="papers/fragments/paperFilesListFragment :: filesList(isLatexAttach = ${true})"/>
</div>
<div class="form-group">
<label for="latex-loader">Загрузить файлы:</label>
<div id="latex-loader">
</div>
</div>
<div class="form-group">
<button id="pdfBtn" class="btn btn-primary text-uppercase"
onclick="generatePDF()"
type="button">
<i id="pdfLoadingIcon" class='fa fa-circle-o-notch fa-spin'
style="display: none;"></i>
pdf
</button>
</div>
</div>
</div>
</div>
@ -207,14 +238,26 @@
showFeedbackMessage("Файл успешно загружен");
console.debug(response);
addNewFile(response);
addNewFile(response, $("#files-list"), false);
}
});
new FileLoader({
div: "latex-loader",
url: urlFileUpload,
maxSize: -1,
extensions: [],
callback: function (response) {
showFeedbackMessage("Файл успешно загружен");
console.debug(response);
addNewFile(response, $("#latex-files-list"), true);
}
});
$('.selectpicker').selectpicker();
});
/*]]>*/
function addNewFile(fileDto) {
var fileNumber = $("#files-list div.row").length;
function addNewFile(fileDto, listElement, isLatexAttach) {
var fileNumber = $('.files-list div.row').length;
var newFileRow = $("<div/>")
.attr("id", 'files' + fileNumber)
@ -248,6 +291,13 @@
.attr("name", "files[" + fileNumber + "].tmpFileName");
newFileRow.append(tmpFileNameInput);
var isLatexInput = $("<input/>")
.attr("type", "hidden")
.attr("id", "files" + fileNumber + ".isLatexAttach")
.attr("value", isLatexAttach)
.attr("name", "files[" + fileNumber + "].isLatexAttach");
newFileRow.append(isLatexInput);
var nextDiv = $("<div/>")
.addClass("col-2");
@ -266,7 +316,7 @@
.attr("onclick", "downloadFile('" + fileDto.tmpFileName + "',null,'" + fileDto.fileName + "')"));
newFileRow.append(nameDiv);
$("#files-list").append(newFileRow);
listElement.append(newFileRow);
}
@ -298,6 +348,37 @@
}
}
}
function generatePDF() {
$('#pdfLoadingIcon').show();
$('#pdfBtn').prop('disabled', true);
var formData = new FormData(document.forms.paperform);
var xhr = new XMLHttpRequest();
xhr.open("POST", urlPdfGenerating);
xhr.send(formData);
xhr.responseType = 'blob';
xhr.onload = function () {
if (this.status == 200) {
console.debug(this.response);
var blob = new Blob([this.response], {type: 'application/pdf'});
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = $('#title').val() + '.pdf';
a.click();
window.URL.revokeObjectURL(url);
} else {
showFeedbackMessage("Ошибка при создании PDF", MessageTypesEnum.DANGER);
}
$('#pdfLoadingIcon').hide();
$('#pdfBtn').prop('disabled', false);
}
}
</script>
</div>

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div class="container" layout:fragment="content">
<section id="services">
<div class="container">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Проекты</h2>
<div th:replace="projects/fragments/projectNavigationFragment"/>
</div>
<div class="row justify-content-center" id="dashboard">
<th:block>
<div/>
</th:block>
</div>
</div>
</section>
</div>
</body>
</html>

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<div th:fragment="projectLine (project)" class="row text-left project-row" style="background-color: white;">
<div class="col">
<span th:replace="projects/fragments/projectStatusFragment :: projectStatus(projectStatus=${project.status})"/>
<a th:href="@{'project?id='+${project.id}}">
<span class="h6" th:text="${project.title}"/>
<span class="text-muted" th:text="${project.description}"/>
</a>
<input class="id-class" type="hidden" th:value="${project.id}"/>
</div>
</div>
</body>
</html>

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./projects" class="btn btn-light toolbar-button"><i class="fa fa-list-alt"></i>
Список</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./dashboard" class="btn btn-light toolbar-button"><i class="fa fa-newspaper-o"
aria-hidden="true"></i> Панель
управления</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./project?id=0" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle"
aria-hidden="true"></i>
Добавить проект</a>
</div>
</div>
</body>
</html>

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<span th:fragment="paperStatus (paperStatus)" class="fa-stack fa-1x">
<th:block th:switch="${projectStatus.name()}">
<div th:case="'APPLICATION'">
<i class="fa fa-circle fa-stack-2x text-draft"></i>
</div>
<div th:case="'ON_COMPETITION'">
<i class="fa fa-circle fa-stack-2x text-review"></i>
</div>
<div th:case="'SUCCESSFUL_PASSAGE'">
<i class="fa fa-circle fa-stack-2x text-accepted"></i>
</div>
<div th:case="'IN_WORK'">
<i class="fa fa-circle fa-stack-2x text-primary"></i>
</div>
<div th:case="'COMPLETED'">
<i class="fa fa-circle fa-stack-2x text-success"></i>
</div>
<div th:case="'FAILED'">
<i class="fa fa-circle fa-stack-2x text-failed"></i>
</div>
</th:block>
<i class="fa fa-file-text-o fa-stack-1x fa-inverse"></i>
</span>
</body>
</html>

@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head>
</head>
<body>
<div class="container" layout:fragment="content">
<section id="paper">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Редактирование проекта</h2>
<div th:replace="projects/fragments/projectNavigationFragment"/>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<form id="project-form" method="post"
th:action="@{'/projects/project?id='+ *{id == null ? '' : id} + ''}"
th:object="${projectDto}">
<div class="row">
<div class="col-md-6 col-sm-12">
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label for="title">Название:</label>
<input class="form-control" id="title" type="text"
placeholder="Название проекта"
th:field="*{title}"/>
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label for="status">Статус:</label>
<select class="form-control" th:field="*{status}" id="status">
<option th:each="status : ${allStatuses}" th:value="${status}"
th:text="${status.statusName}">Status
</option>
</select>
</div>
<div class="form-group">
<label for="description">Описание:</label>
<textarea class="form-control" rows="3" id="description"
th:field="*{description}"></textarea>
</div>
<div class="form-group">
<label for="createGrant">Добавить грант:</label>
<div th:if="*{grant} == null">
<input type="submit" id="createGrant" name="createGrant" class="btn btn-primary"
value="Добавить грант"/>
</div>
<input type="hidden" th:field="*{grant.id}"/>
</div>
<div class="form-group">
<label for="repository">Ссылка на репозиторий:</label>
<input class="form-control" id="repository" type="text"
placeholder="http://"
th:field="*{repository}"/>
<p th:if="${#fields.hasErrors('repository')}" th:errors="*{repository}"
class="alert alert-danger">Incorrect repository link</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label>Дедлайны показателей:</label>
<div class="row">
<input type="hidden"/>
<div class="col-6">
<input type="date" class="form-control" name="deadline"/>
</div>
<div class="col-4">
<input class="form-control" type="text" placeholder="Описание"/>
</div>
<div class="col-2">
<a class="btn btn-danger float-right"><span
aria-hidden="true"><i class="fa fa-times"/></span>
</a>
</div>
</div>
<p th:if="${#fields.hasErrors('deadlines')}" th:errors="*{deadlines}"
class="alert alert-danger">Incorrect title</p>
</div>
<div class="form-group">
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
value="Добавить
дедлайн"/>
</div>
<div class="form-group">
<label for="loader">Загрузить файл:</label>
<div id="loader">
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="col-lg-12">
<div class="form-group">
<button id="sendMessageButton" name="save" class="btn btn-success text-uppercase"
type="submit">
Сохранить
</button>
<button id="cancelButton" class="btn btn-default text-uppercase"
href="/projects/projects">
Отмена
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
<script type="text/javascript" src="/js/file-loader.js"></script>
<script>
/*<![CDATA[*/
$(document).ready(function () {
new FileLoader({
div: "loader",
url: urlFileUpload,
maxSize: 2,
extensions: ["doc", "docx", "xls", "jpg", "png", "pdf", "txt"],
callback: function (response) {
showFeedbackMessage("Файл успешно загружен");
console.debug(response);
}
});
$('.selectpicker').selectpicker();
});
/*]]>*/
</script>
</div>
</body>
</html>

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default" xmlns:th="">
<head>
</head>
<body>
<div class="container" layout:fragment="content">
<form id="projects-form" method="post" th:action="@{'/projects/projects'}">
<input th:type="hidden" name="projectDeleteId" id="projectDeleteId"/>
<section id="projects">
<div class="container">
<div class="row" id="project-list">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Проекты</h2>
<div th:replace="projects/fragments/projectNavigationFragment"/>
</div>
</div>
<div class="row">
<div class="col-md-9 col-sm-12">
<th:block th:each="project : ${projects}">
<div th:replace="projects/fragments/projectLineFragment :: projectLine(project=${project})"/>
</th:block>
</div>
<div class="col-md-3 col-sm-12">
</div>
</div>
</div>
</section>
</form>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save