Merge remote-tracking branch 'origin/dev' into 91-users-info

This commit is contained in:
Artem.Arefev 2019-05-27 00:42:54 +04:00
commit 0e7fdac980
29 changed files with 1040 additions and 137 deletions

View File

@ -21,4 +21,5 @@ public class Constants {
public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest"; public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest";
public static final String PASSWORD_RESET_PAGE = "/reset"; public static final String PASSWORD_RESET_PAGE = "/reset";
public static final int RESET_KEY_LENGTH = 6;
} }

View File

@ -104,7 +104,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.antMatchers("/css/**") .antMatchers("/css/**")
.antMatchers("/js/**") .antMatchers("/js/**")
.antMatchers("/templates/**") .antMatchers("/templates/**")
.antMatchers("/webjars/**"); .antMatchers("/webjars/**")
.antMatchers("/img/**");
} }
@Autowired @Autowired

View File

@ -35,11 +35,6 @@ public class AdviceController {
this.userService = userService; this.userService = userService;
} }
@ModelAttribute("currentUser")
public String getCurrentUser() {
return userService.getCurrentUser().getUserAbbreviate();
}
@ModelAttribute("flashMessage") @ModelAttribute("flashMessage")
public String getFlashMessage() { public String getFlashMessage() {
return null; return null;

View File

@ -9,9 +9,9 @@ public enum ErrorConstants {
USER_EMAIL_EXISTS(102, "Пользователь с таким почтовым ящиком уже существует"), USER_EMAIL_EXISTS(102, "Пользователь с таким почтовым ящиком уже существует"),
USER_LOGIN_EXISTS(103, "Пользователь с таким логином уже существует"), USER_LOGIN_EXISTS(103, "Пользователь с таким логином уже существует"),
USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "Пароли введены неверно"), USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "Пароли введены неверно"),
USER_NOT_FOUND(105, "User is not found"), USER_NOT_FOUND(105, "Аккаунт не найден"),
USER_NOT_ACTIVATED(106, "User is not activated"), USER_NOT_ACTIVATED(106, "User is not activated"),
USER_RESET_ERROR(107, "Invalid reset key"), USER_RESET_ERROR(107, "Некорректный ключ подтверждения"),
USER_UNDEAD_ERROR(108, "Can't edit/delete that user"), USER_UNDEAD_ERROR(108, "Can't edit/delete that user"),
FILE_UPLOAD_ERROR(110, "File upload error"), FILE_UPLOAD_ERROR(110, "File upload error"),
USER_SENDING_MAIL_EXCEPTION(111, "Во время отправки приглашения пользователю произошла ошибка"); USER_SENDING_MAIL_EXCEPTION(111, "Во время отправки приглашения пользователю произошла ошибка");

View File

@ -13,9 +13,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import ru.ulstu.conference.service.ConferenceService; import ru.ulstu.conference.service.ConferenceService;
import ru.ulstu.deadline.model.Deadline; import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.paper.model.AutoCompleteData;
import ru.ulstu.paper.model.Paper; import ru.ulstu.paper.model.Paper;
import ru.ulstu.paper.model.PaperDto; import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperListDto; import ru.ulstu.paper.model.PaperListDto;
import ru.ulstu.paper.model.ReferenceDto;
import ru.ulstu.paper.service.LatexService; import ru.ulstu.paper.service.LatexService;
import ru.ulstu.paper.service.PaperService; import ru.ulstu.paper.service.PaperService;
import ru.ulstu.user.model.User; import ru.ulstu.user.model.User;
@ -105,6 +107,15 @@ public class PaperController {
return "/papers/paper"; return "/papers/paper";
} }
@PostMapping(value = "/paper", params = "addReference")
public String addReference(@Valid PaperDto paperDto, Errors errors) {
if (errors.hasErrors()) {
return "/papers/paper";
}
paperDto.getReferences().add(new ReferenceDto());
return "/papers/paper";
}
@ModelAttribute("allStatuses") @ModelAttribute("allStatuses")
public List<Paper.PaperStatus> getPaperStatuses() { public List<Paper.PaperStatus> getPaperStatuses() {
return paperService.getPaperStatuses(); return paperService.getPaperStatuses();
@ -129,6 +140,16 @@ public class PaperController {
return years; return years;
} }
@ModelAttribute("allFormatStandards")
public List<ReferenceDto.FormatStandard> getFormatStandards() {
return paperService.getFormatStandards();
}
@ModelAttribute("allReferenceTypes")
public List<ReferenceDto.ReferenceType> getReferenceTypes() {
return paperService.getReferenceTypes();
}
@PostMapping("/generatePdf") @PostMapping("/generatePdf")
public ResponseEntity<byte[]> getPdfFile(PaperDto paper) throws IOException, InterruptedException { public ResponseEntity<byte[]> getPdfFile(PaperDto paper) throws IOException, InterruptedException {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
@ -137,6 +158,16 @@ public class PaperController {
return new ResponseEntity<>(latexService.generatePdfFromLatexFile(paper), headers, HttpStatus.OK); return new ResponseEntity<>(latexService.generatePdfFromLatexFile(paper), headers, HttpStatus.OK);
} }
@PostMapping("/getFormattedReferences")
public ResponseEntity<String> getFormattedReferences(PaperDto paperDto) {
return new ResponseEntity<>(paperService.getFormattedReferences(paperDto), new HttpHeaders(), HttpStatus.OK);
}
@ModelAttribute("autocompleteData")
public AutoCompleteData getAutocompleteData() {
return paperService.getAutoCompleteData();
}
private void filterEmptyDeadlines(PaperDto paperDto) { private void filterEmptyDeadlines(PaperDto paperDto) {
paperDto.setDeadlines(paperDto.getDeadlines().stream() paperDto.setDeadlines(paperDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))

View File

@ -0,0 +1,43 @@
package ru.ulstu.paper.model;
import java.util.ArrayList;
import java.util.List;
public class AutoCompleteData {
private List<String> authors = new ArrayList<>();
private List<String> publicationTitles = new ArrayList<>();
private List<String> publishers = new ArrayList<>();
private List<String> journalOrCollectionTitles = new ArrayList<>();
public List<String> getAuthors() {
return authors;
}
public void setAuthors(List<String> authors) {
this.authors = authors;
}
public List<String> getPublicationTitles() {
return publicationTitles;
}
public void setPublicationTitles(List<String> publicationTitles) {
this.publicationTitles = publicationTitles;
}
public List<String> getPublishers() {
return publishers;
}
public void setPublishers(List<String> publishers) {
this.publishers = publishers;
}
public List<String> getJournalOrCollectionTitles() {
return journalOrCollectionTitles;
}
public void setJournalOrCollectionTitles(List<String> journalOrCollectionTitles) {
this.journalOrCollectionTitles = journalOrCollectionTitles;
}
}

View File

@ -123,6 +123,11 @@ public class Paper extends BaseEntity implements UserContainer {
@ManyToMany(mappedBy = "papers") @ManyToMany(mappedBy = "papers")
private List<Grant> grants; private List<Grant> grants;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "paper_id", unique = true)
@Fetch(FetchMode.SUBSELECT)
private List<Reference> references = new ArrayList<>();
public PaperStatus getStatus() { public PaperStatus getStatus() {
return status; return status;
} }
@ -248,6 +253,14 @@ public class Paper extends BaseEntity implements UserContainer {
return getAuthors(); return getAuthors();
} }
public List<Reference> getReferences() {
return references;
}
public void setReferences(List<Reference> references) {
this.references = references;
}
public Optional<Deadline> getNextDeadline() { public Optional<Deadline> getNextDeadline() {
return deadlines return deadlines
.stream() .stream()

View File

@ -38,6 +38,8 @@ public class PaperDto {
private Set<UserDto> authors; private Set<UserDto> authors;
private Integer filterAuthorId; private Integer filterAuthorId;
private String latexText; private String latexText;
private List<ReferenceDto> references = new ArrayList<>();
private ReferenceDto.FormatStandard formatStandard = ReferenceDto.FormatStandard.GOST;
public PaperDto() { public PaperDto() {
deadlines.add(new Deadline()); deadlines.add(new Deadline());
@ -57,7 +59,9 @@ public class PaperDto {
@JsonProperty("locked") Boolean locked, @JsonProperty("locked") Boolean locked,
@JsonProperty("files") List<FileDataDto> files, @JsonProperty("files") List<FileDataDto> files,
@JsonProperty("authorIds") Set<Integer> authorIds, @JsonProperty("authorIds") Set<Integer> authorIds,
@JsonProperty("authors") Set<UserDto> authors) { @JsonProperty("authors") Set<UserDto> authors,
@JsonProperty("references") List<ReferenceDto> references,
@JsonProperty("formatStandard") ReferenceDto.FormatStandard formatStandard) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.status = status; this.status = status;
@ -71,6 +75,8 @@ public class PaperDto {
this.locked = locked; this.locked = locked;
this.files = files; this.files = files;
this.authors = authors; this.authors = authors;
this.references = references;
this.formatStandard = formatStandard;
} }
public PaperDto(Paper paper) { public PaperDto(Paper paper) {
@ -88,6 +94,7 @@ public class PaperDto {
this.files = convert(paper.getFiles(), FileDataDto::new); this.files = convert(paper.getFiles(), FileDataDto::new);
this.authorIds = convert(paper.getAuthors(), user -> user.getId()); this.authorIds = convert(paper.getAuthors(), user -> user.getId());
this.authors = convert(paper.getAuthors(), UserDto::new); this.authors = convert(paper.getAuthors(), UserDto::new);
this.references = convert(paper.getReferences(), ReferenceDto::new);
} }
public Integer getId() { public Integer getId() {
@ -216,4 +223,20 @@ public class PaperDto {
public void setFilterAuthorId(Integer filterAuthorId) { public void setFilterAuthorId(Integer filterAuthorId) {
this.filterAuthorId = filterAuthorId; this.filterAuthorId = filterAuthorId;
} }
public List<ReferenceDto> getReferences() {
return references;
}
public void setReferences(List<ReferenceDto> references) {
this.references = references;
}
public ReferenceDto.FormatStandard getFormatStandard() {
return formatStandard;
}
public void setFormatStandard(ReferenceDto.FormatStandard formatStandard) {
this.formatStandard = formatStandard;
}
} }

View File

@ -0,0 +1,88 @@
package ru.ulstu.paper.model;
import ru.ulstu.core.model.BaseEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
@Entity
public class Reference extends BaseEntity {
private String authors;
@Column(name = "publication_title")
private String publicationTitle;
@Column(name = "publication_year")
private Integer publicationYear;
private String publisher;
private String pages;
@Column(name = "journal_or_collection_title")
private String journalOrCollectionTitle;
@Enumerated(value = EnumType.STRING)
@Column(name = "reference_type")
private ReferenceDto.ReferenceType referenceType = ReferenceDto.ReferenceType.ARTICLE;
public String getAuthors() {
return authors;
}
public void setAuthors(String authors) {
this.authors = authors;
}
public String getPublicationTitle() {
return publicationTitle;
}
public void setPublicationTitle(String publicationTitle) {
this.publicationTitle = publicationTitle;
}
public Integer getPublicationYear() {
return publicationYear;
}
public void setPublicationYear(Integer publicationYear) {
this.publicationYear = publicationYear;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public String getPages() {
return pages;
}
public void setPages(String pages) {
this.pages = pages;
}
public String getJournalOrCollectionTitle() {
return journalOrCollectionTitle;
}
public void setJournalOrCollectionTitle(String journalOrCollectionTitle) {
this.journalOrCollectionTitle = journalOrCollectionTitle;
}
public ReferenceDto.ReferenceType getReferenceType() {
return referenceType;
}
public void setReferenceType(ReferenceDto.ReferenceType referenceType) {
this.referenceType = referenceType;
}
}

View File

@ -34,6 +34,7 @@ public class ReferenceDto {
} }
} }
private Integer id;
private String authors; private String authors;
private String publicationTitle; private String publicationTitle;
private Integer publicationYear; private Integer publicationYear;
@ -42,9 +43,11 @@ public class ReferenceDto {
private String journalOrCollectionTitle; private String journalOrCollectionTitle;
private ReferenceType referenceType; private ReferenceType referenceType;
private FormatStandard formatStandard; private FormatStandard formatStandard;
private boolean deleted;
@JsonCreator @JsonCreator
public ReferenceDto( public ReferenceDto(
@JsonProperty("id") Integer id,
@JsonProperty("authors") String authors, @JsonProperty("authors") String authors,
@JsonProperty("publicationTitle") String publicationTitle, @JsonProperty("publicationTitle") String publicationTitle,
@JsonProperty("publicationYear") Integer publicationYear, @JsonProperty("publicationYear") Integer publicationYear,
@ -52,7 +55,9 @@ public class ReferenceDto {
@JsonProperty("pages") String pages, @JsonProperty("pages") String pages,
@JsonProperty("journalOrCollectionTitle") String journalOrCollectionTitle, @JsonProperty("journalOrCollectionTitle") String journalOrCollectionTitle,
@JsonProperty("referenceType") ReferenceType referenceType, @JsonProperty("referenceType") ReferenceType referenceType,
@JsonProperty("formatStandard") FormatStandard formatStandard) { @JsonProperty("formatStandard") FormatStandard formatStandard,
@JsonProperty("isDeleted") boolean deleted) {
this.id = id;
this.authors = authors; this.authors = authors;
this.publicationTitle = publicationTitle; this.publicationTitle = publicationTitle;
this.publicationYear = publicationYear; this.publicationYear = publicationYear;
@ -61,6 +66,22 @@ public class ReferenceDto {
this.journalOrCollectionTitle = journalOrCollectionTitle; this.journalOrCollectionTitle = journalOrCollectionTitle;
this.referenceType = referenceType; this.referenceType = referenceType;
this.formatStandard = formatStandard; this.formatStandard = formatStandard;
this.deleted = deleted;
}
public ReferenceDto(Reference reference) {
this.id = reference.getId();
this.authors = reference.getAuthors();
this.publicationTitle = reference.getPublicationTitle();
this.publicationYear = reference.getPublicationYear();
this.publisher = reference.getPublisher();
this.pages = reference.getPages();
this.journalOrCollectionTitle = reference.getJournalOrCollectionTitle();
this.referenceType = reference.getReferenceType();
}
public ReferenceDto() {
referenceType = ReferenceType.ARTICLE;
} }
public String getAuthors() { public String getAuthors() {
@ -126,4 +147,20 @@ public class ReferenceDto {
public void setFormatStandard(FormatStandard formatStandard) { public void setFormatStandard(FormatStandard formatStandard) {
this.formatStandard = formatStandard; this.formatStandard = formatStandard;
} }
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public boolean getDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
} }

View File

@ -0,0 +1,23 @@
package ru.ulstu.paper.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import ru.ulstu.paper.model.Reference;
import java.util.List;
public interface ReferenceRepository extends JpaRepository<Reference, Integer> {
void deleteById(Integer id);
@Query("SELECT DISTINCT r.authors FROM Reference r")
List<String> findDistinctAuthors();
@Query("SELECT DISTINCT r.publicationTitle FROM Reference r")
List<String> findDistinctPublicationTitles();
@Query("SELECT DISTINCT r.publisher FROM Reference r")
List<String> findDistinctPublishers();
@Query("SELECT DISTINCT r.journalOrCollectionTitle FROM Reference r where r.journalOrCollectionTitle <> ''")
List<String> findDistinctJournalOrCollectionTitles();
}

View File

@ -7,11 +7,14 @@ import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.deadline.service.DeadlineService; import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.file.model.FileDataDto; import ru.ulstu.file.model.FileDataDto;
import ru.ulstu.file.service.FileService; import ru.ulstu.file.service.FileService;
import ru.ulstu.paper.model.AutoCompleteData;
import ru.ulstu.paper.model.Paper; import ru.ulstu.paper.model.Paper;
import ru.ulstu.paper.model.PaperDto; import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperListDto; import ru.ulstu.paper.model.PaperListDto;
import ru.ulstu.paper.model.Reference;
import ru.ulstu.paper.model.ReferenceDto; import ru.ulstu.paper.model.ReferenceDto;
import ru.ulstu.paper.repository.PaperRepository; import ru.ulstu.paper.repository.PaperRepository;
import ru.ulstu.paper.repository.ReferenceRepository;
import ru.ulstu.timeline.service.EventService; import ru.ulstu.timeline.service.EventService;
import ru.ulstu.user.model.User; import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService; import ru.ulstu.user.service.UserService;
@ -39,6 +42,7 @@ import static ru.ulstu.paper.model.ReferenceDto.ReferenceType.ARTICLE;
import static ru.ulstu.paper.model.ReferenceDto.ReferenceType.BOOK; import static ru.ulstu.paper.model.ReferenceDto.ReferenceType.BOOK;
@Service @Service
@Transactional
public class PaperService { public class PaperService {
private final static int MAX_DISPLAY_SIZE = 40; private final static int MAX_DISPLAY_SIZE = 40;
private final static String PAPER_FORMATTED_TEMPLATE = "%s %s"; private final static String PAPER_FORMATTED_TEMPLATE = "%s %s";
@ -49,14 +53,17 @@ public class PaperService {
private final DeadlineService deadlineService; private final DeadlineService deadlineService;
private final FileService fileService; private final FileService fileService;
private final EventService eventService; private final EventService eventService;
private final ReferenceRepository referenceRepository;
public PaperService(PaperRepository paperRepository, public PaperService(PaperRepository paperRepository,
ReferenceRepository referenceRepository,
FileService fileService, FileService fileService,
PaperNotificationService paperNotificationService, PaperNotificationService paperNotificationService,
UserService userService, UserService userService,
DeadlineService deadlineService, DeadlineService deadlineService,
EventService eventService) { EventService eventService) {
this.paperRepository = paperRepository; this.paperRepository = paperRepository;
this.referenceRepository = referenceRepository;
this.fileService = fileService; this.fileService = fileService;
this.paperNotificationService = paperNotificationService; this.paperNotificationService = paperNotificationService;
this.userService = userService; this.userService = userService;
@ -117,6 +124,7 @@ public class PaperService {
paper.setTitle(paperDto.getTitle()); paper.setTitle(paperDto.getTitle());
paper.setUpdateDate(new Date()); paper.setUpdateDate(new Date());
paper.setDeadlines(deadlineService.saveOrCreate(paperDto.getDeadlines())); paper.setDeadlines(deadlineService.saveOrCreate(paperDto.getDeadlines()));
paper.setReferences(saveOrCreateReferences(paperDto.getReferences()));
paper.setFiles(fileService.saveOrCreate(paperDto.getFiles().stream() paper.setFiles(fileService.saveOrCreate(paperDto.getFiles().stream()
.filter(f -> !f.isDeleted()) .filter(f -> !f.isDeleted())
.collect(toList()))); .collect(toList())));
@ -127,6 +135,41 @@ public class PaperService {
return paper; return paper;
} }
public List<Reference> saveOrCreateReferences(List<ReferenceDto> references) {
return references
.stream()
.filter(reference -> !reference.getDeleted())
.map(reference -> reference.getId() != null ? updateReference(reference) : createReference(reference))
.collect(Collectors.toList());
}
@Transactional
public Reference updateReference(ReferenceDto referenceDto) {
Reference updateReference = referenceRepository.findOne(referenceDto.getId());
copyFromDto(updateReference, referenceDto);
referenceRepository.save(updateReference);
return updateReference;
}
@Transactional
public Reference createReference(ReferenceDto referenceDto) {
Reference newReference = new Reference();
copyFromDto(newReference, referenceDto);
newReference = referenceRepository.save(newReference);
return newReference;
}
private Reference copyFromDto(Reference reference, ReferenceDto referenceDto) {
reference.setAuthors(referenceDto.getAuthors());
reference.setJournalOrCollectionTitle(referenceDto.getJournalOrCollectionTitle());
reference.setPages(referenceDto.getPages());
reference.setPublicationTitle(referenceDto.getPublicationTitle());
reference.setPublicationYear(referenceDto.getPublicationYear());
reference.setPublisher(referenceDto.getPublisher());
reference.setReferenceType(referenceDto.getReferenceType());
return reference;
}
@Transactional @Transactional
public Integer update(PaperDto paperDto) throws IOException { public Integer update(PaperDto paperDto) throws IOException {
Paper paper = paperRepository.findOne(paperDto.getId()); Paper paper = paperRepository.findOne(paperDto.getId());
@ -139,6 +182,11 @@ public class PaperService {
fileService.delete(file.getId()); fileService.delete(file.getId());
} }
paperRepository.save(copyFromDto(paper, paperDto)); paperRepository.save(copyFromDto(paper, paperDto));
for (ReferenceDto referenceDto : paperDto.getReferences().stream()
.filter(f -> f.getDeleted() && f.getId() != null)
.collect(toList())) {
referenceRepository.deleteById(referenceDto.getId());
}
eventService.updatePaperDeadlines(paper); eventService.updatePaperDeadlines(paper);
paper.getAuthors().forEach(author -> { paper.getAuthors().forEach(author -> {
@ -168,6 +216,14 @@ public class PaperService {
return Arrays.asList(Paper.PaperType.values()); return Arrays.asList(Paper.PaperType.values());
} }
public List<ReferenceDto.FormatStandard> getFormatStandards() {
return Arrays.asList(ReferenceDto.FormatStandard.values());
}
public List<ReferenceDto.ReferenceType> getReferenceTypes() {
return Arrays.asList(ReferenceDto.ReferenceType.values());
}
@Transactional @Transactional
public Paper create(String title, User user, Date deadlineDate) { public Paper create(String title, User user, Date deadlineDate) {
Paper paper = new Paper(); Paper paper = new Paper();
@ -287,12 +343,23 @@ public class PaperService {
: getSpringerReference(referenceDto); : getSpringerReference(referenceDto);
} }
public String getFormattedReferences(PaperDto paperDto) {
return String.join("\r\n", paperDto.getReferences()
.stream()
.filter(r -> !r.getDeleted())
.map(r -> {
r.setFormatStandard(paperDto.getFormatStandard());
return getFormattedReference(r);
})
.collect(Collectors.toList()));
}
public String getGostReference(ReferenceDto referenceDto) { public String getGostReference(ReferenceDto referenceDto) {
return MessageFormat.format(referenceDto.getReferenceType() == BOOK ? "{0} {1} - {2}{3}. - {4}с." : "{0} {1}{5} {2}{3}. С. {4}.", return MessageFormat.format(referenceDto.getReferenceType() == BOOK ? "{0} {1} - {2}{3}. - {4}с." : "{0} {1}{5} {2}{3}. С. {4}.",
referenceDto.getAuthors(), referenceDto.getAuthors(),
referenceDto.getPublicationTitle(), referenceDto.getPublicationTitle(),
StringUtils.isEmpty(referenceDto.getPublisher()) ? "" : referenceDto.getPublisher() + ", ", StringUtils.isEmpty(referenceDto.getPublisher()) ? "" : referenceDto.getPublisher() + ", ",
referenceDto.getPublicationYear().toString(), referenceDto.getPublicationYear() != null ? referenceDto.getPublicationYear().toString() : "",
referenceDto.getPages(), referenceDto.getPages(),
StringUtils.isEmpty(referenceDto.getJournalOrCollectionTitle()) ? "." : " // " + referenceDto.getJournalOrCollectionTitle() + "."); StringUtils.isEmpty(referenceDto.getJournalOrCollectionTitle()) ? "." : " // " + referenceDto.getJournalOrCollectionTitle() + ".");
} }
@ -300,7 +367,7 @@ public class PaperService {
public String getSpringerReference(ReferenceDto referenceDto) { public String getSpringerReference(ReferenceDto referenceDto) {
return MessageFormat.format("{0} ({1}) {2}.{3} {4}pp {5}", return MessageFormat.format("{0} ({1}) {2}.{3} {4}pp {5}",
referenceDto.getAuthors(), referenceDto.getAuthors(),
referenceDto.getPublicationYear().toString(), referenceDto.getPublicationYear() != null ? referenceDto.getPublicationYear().toString() : "",
referenceDto.getPublicationTitle(), referenceDto.getPublicationTitle(),
referenceDto.getReferenceType() == ARTICLE ? " " + referenceDto.getJournalOrCollectionTitle() + "," : "", referenceDto.getReferenceType() == ARTICLE ? " " + referenceDto.getJournalOrCollectionTitle() + "," : "",
StringUtils.isEmpty(referenceDto.getPublisher()) ? "" : referenceDto.getPublisher() + ", ", StringUtils.isEmpty(referenceDto.getPublisher()) ? "" : referenceDto.getPublisher() + ", ",
@ -310,4 +377,13 @@ public class PaperService {
public List<Paper> findAllCompletedByType(Paper.PaperType type) { public List<Paper> findAllCompletedByType(Paper.PaperType type) {
return paperRepository.findByTypeAndStatus(type, Paper.PaperStatus.COMPLETED); return paperRepository.findByTypeAndStatus(type, Paper.PaperStatus.COMPLETED);
} }
public AutoCompleteData getAutoCompleteData() {
AutoCompleteData autoCompleteData = new AutoCompleteData();
autoCompleteData.setAuthors(referenceRepository.findDistinctAuthors());
autoCompleteData.setJournalOrCollectionTitles(referenceRepository.findDistinctJournalOrCollectionTitles());
autoCompleteData.setPublicationTitles(referenceRepository.findDistinctPublicationTitles());
autoCompleteData.setPublishers(referenceRepository.findDistinctPublishers());
return autoCompleteData;
}
} }

View File

@ -10,6 +10,7 @@ import ru.ulstu.tags.model.Tag;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -135,6 +136,29 @@ public class TaskDto {
this.tags = tags; this.tags = tags;
} }
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TaskDto taskDto = (TaskDto) o;
return Objects.equals(id, taskDto.id) &&
Objects.equals(title, taskDto.title) &&
Objects.equals(description, taskDto.description) &&
status == taskDto.status &&
Objects.equals(deadlines, taskDto.deadlines) &&
Objects.equals(tagIds, taskDto.tagIds) &&
Objects.equals(tags, taskDto.tags);
}
@Override
public int hashCode() {
return Objects.hash(id, title, description, status, deadlines, createDate, updateDate, tagIds, tags);
}
public String getTagsString() { public String getTagsString() {
return StringUtils.abbreviate(tags return StringUtils.abbreviate(tags
.stream() .stream()

View File

@ -108,7 +108,7 @@ public class TaskService {
} }
@Transactional @Transactional
public void delete(Integer taskId) throws IOException { public boolean delete(Integer taskId) throws IOException {
if (taskRepository.exists(taskId)) { if (taskRepository.exists(taskId)) {
Task scheduleTask = taskRepository.findOne(taskId); Task scheduleTask = taskRepository.findOne(taskId);
Scheduler sch = schedulerRepository.findOneByTask(scheduleTask); Scheduler sch = schedulerRepository.findOneByTask(scheduleTask);
@ -116,7 +116,9 @@ public class TaskService {
schedulerRepository.delete(sch.getId()); schedulerRepository.delete(sch.getId());
} }
taskRepository.delete(taskId); taskRepository.delete(taskId);
return true;
} }
return false;
} }
@ -128,14 +130,14 @@ public class TaskService {
} }
} }
public void copyMainPart(Task newTask, Task task) { private void copyMainPart(Task newTask, Task task) {
newTask.setTitle(task.getTitle()); newTask.setTitle(task.getTitle());
newTask.setTags(tagService.saveOrCreate(task.getTags())); newTask.setTags(tagService.saveOrCreate(task.getTags()));
newTask.setCreateDate(new Date()); newTask.setCreateDate(new Date());
newTask.setStatus(Task.TaskStatus.LOADED_FROM_KIAS); newTask.setStatus(Task.TaskStatus.LOADED_FROM_KIAS);
} }
public Task copyTaskWithNewDates(Task task) { private Task copyTaskWithNewDates(Task task) {
Task newTask = new Task(); Task newTask = new Task();
copyMainPart(newTask, task); copyMainPart(newTask, task);
Calendar cal1 = DateUtils.getCalendar(newTask.getCreateDate()); Calendar cal1 = DateUtils.getCalendar(newTask.getCreateDate());
@ -157,7 +159,7 @@ public class TaskService {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
public Task copyTaskWithNewYear(Task task) { private Task copyTaskWithNewYear(Task task) {
Task newTask = new Task(); Task newTask = new Task();
copyMainPart(newTask, task); copyMainPart(newTask, task);
newTask.setDeadlines(newYearDeadlines(task.getDeadlines())); newTask.setDeadlines(newYearDeadlines(task.getDeadlines()));
@ -184,7 +186,7 @@ public class TaskService {
} }
@Transactional @Transactional
public void generateYearTasks() { public List<Task> generateYearTasks() {
Set<Tag> tags = checkRepeatingTags(false); Set<Tag> tags = checkRepeatingTags(false);
List<Task> tasks = new ArrayList<>(); List<Task> tasks = new ArrayList<>();
tags.forEach(tag -> { tags.forEach(tag -> {
@ -200,7 +202,9 @@ public class TaskService {
Task newTask = copyTaskWithNewYear(task); Task newTask = copyTaskWithNewYear(task);
taskRepository.save(newTask); taskRepository.save(newTask);
}); });
return tasks;
} }
return null;
} }
@ -245,8 +249,9 @@ public class TaskService {
} }
@Transactional @Transactional
public void createPeriodTask(Scheduler scheduler) { public Task createPeriodTask(Scheduler scheduler) {
Task newTask = copyTaskWithNewDates(scheduler.getTask()); Task newTask = copyTaskWithNewDates(scheduler.getTask());
taskRepository.save(newTask); taskRepository.save(newTask);
return newTask;
} }
} }

View File

@ -148,16 +148,15 @@ public class UserController extends OdinController<UserListDto, UserDto> {
} }
@PostMapping(PASSWORD_RESET_REQUEST_URL) @PostMapping(PASSWORD_RESET_REQUEST_URL)
public Response<Boolean> requestPasswordReset(@RequestParam("email") String email) { public void requestPasswordReset(@RequestParam("email") String email) {
log.debug("REST: UserController.requestPasswordReset( {} )", email); log.debug("REST: UserController.requestPasswordReset( {} )", email);
return new Response<>(userService.requestUserPasswordReset(email)); userService.requestUserPasswordReset(email);
} }
@PostMapping(PASSWORD_RESET_URL) @PostMapping(PASSWORD_RESET_URL)
public Response<Boolean> finishPasswordReset(@RequestParam("key") String key, public Response<Boolean> finishPasswordReset(@RequestBody UserResetPasswordDto userResetPasswordDto) {
@RequestBody UserResetPasswordDto userResetPasswordDto) { log.debug("REST: UserController.requestPasswordReset( {} )", userResetPasswordDto.getResetKey());
log.debug("REST: UserController.requestPasswordReset( {} )", key); return new Response<>(userService.completeUserPasswordReset(userResetPasswordDto));
return new Response<>(userService.completeUserPasswordReset(key, userResetPasswordDto));
} }
@PostMapping("/changePassword") @PostMapping("/changePassword")

View File

@ -14,6 +14,10 @@ public class UserResetPasswordDto {
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50) @Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
private String passwordConfirm; private String passwordConfirm;
@NotEmpty
@Size(min = Constants.RESET_KEY_LENGTH)
private String resetKey;
public String getPassword() { public String getPassword() {
return password; return password;
} }
@ -25,4 +29,8 @@ public class UserResetPasswordDto {
public boolean isPasswordsValid() { public boolean isPasswordsValid() {
return Objects.equals(password, passwordConfirm); return Objects.equals(password, passwordConfirm);
} }
public String getResetKey() {
return resetKey;
}
} }

View File

@ -106,8 +106,7 @@ public class MailService {
sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE); sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE);
} }
@Async public void sendPasswordResetMail(User user) throws MessagingException, MailException {
public void sendPasswordResetMail(User user) {
sendEmailFromTemplate(user, "passwordResetEmail", Constants.MAIL_RESET); sendEmailFromTemplate(user, "passwordResetEmail", Constants.MAIL_RESET);
} }
@ -118,6 +117,5 @@ public class MailService {
@Async @Async
public void sendChangePasswordMail(User user) { public void sendChangePasswordMail(User user) {
sendEmailFromTemplate(user, "passwordChangeEmail", Constants.MAIL_CHANGE_PASSWORD); sendEmailFromTemplate(user, "passwordChangeEmail", Constants.MAIL_CHANGE_PASSWORD);
} }
} }

View File

@ -252,7 +252,7 @@ public class UserService implements UserDetailsService {
mailService.sendChangePasswordMail(user); mailService.sendChangePasswordMail(user);
} }
public boolean requestUserPasswordReset(String email) { public boolean requestUserPasswordReset(String email) {
User user = userRepository.findOneByEmailIgnoreCase(email); User user = userRepository.findOneByEmailIgnoreCase(email);
if (user == null) { if (user == null) {
throw new UserNotFoundException(email); throw new UserNotFoundException(email);
@ -263,23 +263,30 @@ public class UserService implements UserDetailsService {
user.setResetKey(UserUtils.generateResetKey()); user.setResetKey(UserUtils.generateResetKey());
user.setResetDate(new Date()); user.setResetDate(new Date());
user = userRepository.save(user); user = userRepository.save(user);
mailService.sendPasswordResetMail(user); try {
mailService.sendPasswordResetMail(user);
} catch (MessagingException | MailException e) {
throw new UserSendingMailException(email);
}
log.debug("Created Reset Password Request for User: {}", user.getLogin()); log.debug("Created Reset Password Request for User: {}", user.getLogin());
return true; return true;
} }
public boolean completeUserPasswordReset(String key, UserResetPasswordDto userResetPasswordDto) { public boolean completeUserPasswordReset(UserResetPasswordDto userResetPasswordDto) {
if (!userResetPasswordDto.isPasswordsValid()) { if (!userResetPasswordDto.isPasswordsValid()) {
throw new UserPasswordsNotValidOrNotMatchException(""); throw new UserPasswordsNotValidOrNotMatchException("Пароли не совпадают");
} }
User user = userRepository.findOneByResetKey(key); User user = userRepository.findOneByResetKey(userResetPasswordDto.getResetKey());
if (user == null) { if (user == null) {
throw new UserResetKeyError(key); throw new UserResetKeyError(userResetPasswordDto.getResetKey());
} }
user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword())); user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword()));
user.setResetKey(null); user.setResetKey(null);
user.setResetDate(null); user.setResetDate(null);
user = userRepository.save(user); user = userRepository.save(user);
mailService.sendChangePasswordMail(user);
log.debug("Reset Password for User: {}", user.getLogin()); log.debug("Reset Password for User: {}", user.getLogin());
return true; return true;
} }

View File

@ -0,0 +1,27 @@
<?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="20190523_000000-1">
<createTable tableName="reference">
<column name="id" type="integer">
<constraints nullable="false"/>
</column>
<column name="authors" type="varchar(255)"/>
<column name="publication_title" type="varchar(255)"/>
<column name="publication_year" type="integer"/>
<column name="paper_id" type="integer"/>
<column name="publisher" type="varchar(255)"/>
<column name="pages" type="varchar(255)"/>
<column name="journal_or_collection_title" type="varchar(255)"/>
<column name="reference_type" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="version" type="integer"/>
</createTable>
<addPrimaryKey columnNames="id" constraintName="pk_reference" tableName="reference"/>
<addForeignKeyConstraint baseTableName="reference" baseColumnNames="paper_id"
constraintName="fk_reference_paper_id" referencedTableName="paper"
referencedColumnNames="id"/>
</changeSet>
</databaseChangeLog>

View File

@ -42,4 +42,5 @@
<include file="db/changelog-20190507_000000-schema.xml"/> <include file="db/changelog-20190507_000000-schema.xml"/>
<include file="db/changelog-20190507_000001-schema.xml"/> <include file="db/changelog-20190507_000001-schema.xml"/>
<include file="db/changelog-20190511_000000-schema.xml"/> <include file="db/changelog-20190511_000000-schema.xml"/>
<include file="db/changelog-20190523_000000-schema.xml"/>
</databaseChangeLog> </databaseChangeLog>

View File

@ -1,23 +1,19 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org">
<head> <head>
<title>Password reset</title> <title>Восстановление пароля</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="shortcut icon" th:href="@{|${baseUrl}/favicon.ico|}"/> <link rel="shortcut icon" th:href="@{|${baseUrl}/favicon.ico|}"/>
</head> </head>
<body> <body>
<p> <p>
Dear <span th:text="${user.firstName + ' ' + user.lastName}">Ivan Ivanov</span> Дорогой <span th:text="${user.firstName + ' ' + user.lastName}">Ivan Ivanov</span>
</p> </p>
<p> <p>
For your account a password reset was requested, please click on the URL below to Ваш ключ для восстановления пароля <span th:text="${user.resetKey}"></span>
</p> </p>
<p> <p>
<a th:href="@{|${baseUrl}/reset?key=${user.resetKey}|}" С уважением,
th:text="@{|${baseUrl}/reset?key=${user.resetKey}|}">Reset Link</a>
</p>
<p>
Regards,
<br/> <br/>
<em>Balance Team.</em> <em>Balance Team.</em>
</p> </p>

View File

@ -0,0 +1,3 @@
.loader {
padding-left:50%
}

View File

@ -6,4 +6,35 @@
.nav-tabs { .nav-tabs {
margin-bottom: 20px; margin-bottom: 20px;
}
#nav-references label, #nav-references select, #nav-references input {
display: inline-block;
vertical-align: middle;
}
#nav-references .collapse-heading {
border: 1px solid #ddd;
border-radius: 4px;
padding: 5px 15px;
background-color: #efefef;
}
#nav-references .collapse-heading a {
text-decoration: none;
}
#nav-references a:hover {
text-decoration: none;
}
#nav-references #formattedReferencesArea {
height: 150px;
}
.ui-autocomplete {
max-height: 200px;
max-width: 400px;
overflow-y: scroll;
overflow-x: hidden;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

View File

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

View File

@ -1,7 +1,7 @@
function changePassword() { function changePassword() {
oldPassword = document.getElementById("oldPassword").value oldPassword = $("#oldPassword").val()
password = document.getElementById("password").value password = $("#password").val()
confirmPassword = document.getElementById("confirmPassword").value confirmPassword = $("#confirmPassword").val()
if ([oldPassword.length, password.length, confirmPassword.length].includes(0)) { if ([oldPassword.length, password.length, confirmPassword.length].includes(0)) {
showFeedbackMessage("Заполните все поля", MessageTypesEnum.WARNING); showFeedbackMessage("Заполните все поля", MessageTypesEnum.WARNING);
@ -23,7 +23,7 @@ function changePassword() {
}), }),
method: "POST", method: "POST",
success: function() { success: function() {
document.getElementById("closeModalPassword").click(); $("#closeModalPassword").click();
showFeedbackMessage("Пароль был обновлен", MessageTypesEnum.SUCCESS) showFeedbackMessage("Пароль был обновлен", MessageTypesEnum.SUCCESS)
}, },
@ -34,11 +34,8 @@ function changePassword() {
} }
function inviteUser() { function inviteUser() {
email = document.getElementById("email").value; email = $("#email").val();
re = /\S+@\S+\.\S+/; if (!isEmailValid(email)) {
if (!re.test(email)) {
showFeedbackMessage("Некорректный почтовый ящик", MessageTypesEnum.WARNING); showFeedbackMessage("Некорректный почтовый ящик", MessageTypesEnum.WARNING);
return; return;
} }
@ -48,11 +45,83 @@ function inviteUser() {
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
method: "POST", method: "POST",
success: function() { success: function() {
document.getElementById("closeModalInvite").click(); $("#closeModalInvite").click();
showFeedbackMessage("Пользователь был успешно приглашен", MessageTypesEnum.SUCCESS) showFeedbackMessage("Пользователь был успешно приглашен", MessageTypesEnum.SUCCESS)
}, },
error: function(errorData) { error: function(errorData) {
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING) showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
} }
}) })
}
function requestResetPassword() {
email = $("#emailReset").val()
if (!isEmailValid(email)) {
showFeedbackMessage("Некорректный почтовый ящик", MessageTypesEnum.WARNING);
return;
}
$("#dvloader").show();
$.ajax({
url:"/api/1.0/users/password-reset-request?email=" + email,
contentType: "application/json; charset=utf-8",
method: "POST",
success: function() {
showFeedbackMessage("Проверочный код был отправлен на указанный почтовый ящик", MessageTypesEnum.SUCCESS)
$("#passwordNew").show()
$("#passwordConfirm").show()
$("#btnReset").show()
$("#resetKey").show()
$("#emailReset").hide()
$("#btnSend").hide()
$("#dvloader").hide()
},
error: function(errorData) {
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
$("#dvloader").hide()
}
})
}
function resetPassword() {
passwordNew = $("#passwordNew").val();
passwordConfirm = $("#passwordConfirm").val();
resetKey = $("#resetKey").val();
if ([passwordNew, passwordConfirm, resetKey].includes("")) {
showFeedbackMessage("Заполните все поля", MessageTypesEnum.WARNING);
return;
}
if (passwordNew != passwordConfirm) {
showFeedbackMessage("Пароли не совпадают", MessageTypesEnum.WARNING);
return;
}
$.ajax({
url:"/api/1.0/users/password-reset",
contentType: "application/json; charset=utf-8",
method: "POST",
data: JSON.stringify({
"password": passwordNew,
"passwordConfirm": passwordConfirm,
"resetKey": resetKey,
}),
success: function() {
showFeedbackMessage("Пользователь был успешно приглашен", MessageTypesEnum.SUCCESS)
window.location.href = "/login"
},
error: function(errorData) {
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
}
})
}
function isEmailValid(email) {
re = /\S+@\S+\.\S+/;
return re.test(email)
} }

View File

@ -4,6 +4,10 @@
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html"> layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head> <head>
<link rel="stylesheet" href="../css/paper.css"/> <link rel="stylesheet" href="../css/paper.css"/>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"/>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
</head> </head>
<body> <body>
@ -31,6 +35,9 @@
href="#nav-main" role="tab" aria-controls="nav-main" aria-selected="true">Статья</a> 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" <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> href="#nav-latex" role="tab" aria-controls="nav-latex" aria-selected="false">Latex</a>
<a class="nav-item nav-link" id="nav-references-tab" data-toggle="tab"
href="#nav-references" role="tab" aria-controls="nav-references"
aria-selected="false">References</a>
</div> </div>
</nav> </nav>
<div class="tab-content" id="nav-tabContent"> <div class="tab-content" id="nav-tabContent">
@ -71,54 +78,56 @@
th:field="*{comment}"></textarea> th:field="*{comment}"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="title">Ссылка на сайт конференции:</label> <label for="title">Ссылка на сайт конференции:</label>
<input class="form-control" id="url" type="text" <input class="form-control" id="url" type="text"
placeholder="Url" placeholder="Url"
th:field="*{url}"/> th:field="*{url}"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Дедлайны:</label> <label>Дедлайны:</label>
<div class="row" th:each="deadline, rowStat : *{deadlines}"> <div class="row" th:each="deadline, rowStat : *{deadlines}">
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/> <input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
<div class="col-6"> <div class="col-6">
<input type="date" class="form-control" name="deadline" <input type="date" class="form-control" name="deadline"
th:field="*{deadlines[__${rowStat.index}__].date}"/> th:field="*{deadlines[__${rowStat.index}__].date}"/>
</div> </div>
<div class="col-4"> <div class="col-4">
<input class="form-control" type="text" placeholder="Описание" <input class="form-control" type="text" placeholder="Описание"
th:field="*{deadlines[__${rowStat.index}__].description}"/> th:field="*{deadlines[__${rowStat.index}__].description}"/>
</div> </div>
<div class="col-2"> <div class="col-2">
<a class="btn btn-danger float-right" <a class="btn btn-danger float-right"
th:onclick="|$('#deadlines${rowStat.index}\\.description').val(''); th:onclick="|$('#deadlines${rowStat.index}\\.description').val('');
$('#deadlines${rowStat.index}\\.date').val(''); $('#deadlines${rowStat.index}\\.date').val('');
$('#addDeadline').click();|"><span $('#addDeadline').click();|"><span
aria-hidden="true"><i class="fa fa-times"/></span> aria-hidden="true"><i class="fa fa-times"/></span>
</a> </a>
</div>
</div>
<p th:if="${#fields.hasErrors('deadlines')}" th:errors="*{deadlines}"
class="alert alert-danger">Incorrect title</p>
</div> </div>
</div> <div class="form-group">
<p th:if="${#fields.hasErrors('deadlines')}" th:errors="*{deadlines}" <input type="submit" id="addDeadline" name="addDeadline"
class="alert alert-danger">Incorrect title</p> class="btn btn-primary"
</div> value="Добавить
<div class="form-group">
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
value="Добавить
дедлайн"/> дедлайн"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Авторы:</label> <label>Авторы:</label>
<select class="selectpicker form-control" multiple="true" data-live-search="true" <select class="selectpicker form-control" multiple="true"
title="-- Выберите авторов --" data-live-search="true"
th:field="*{authorIds}"> title="-- Выберите авторов --"
<option th:each="author: ${allAuthors}" th:value="${author.id}" th:field="*{authorIds}">
th:text="${author.lastName}">Status <option th:each="author: ${allAuthors}" th:value="${author.id}"
</option> th:text="${author.lastName}">Status
</select> </option>
<p th:if="${#fields.hasErrors('authorIds')}" th:errors="*{authorIds}" </select>
class="alert alert-danger">Incorrect title</p> <p th:if="${#fields.hasErrors('authorIds')}" th:errors="*{authorIds}"
</div> class="alert alert-danger">Incorrect title</p>
</div>
<div class="form-group files-list" id="files-list"> <div class="form-group files-list" id="files-list">
<label>Файлы:</label> <label>Файлы:</label>
@ -167,6 +176,125 @@
</button> </button>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="nav-references" role="tabpanel"
aria-labelledby="nav-profile-tab">
<div class="form-group">
<label>References:</label>
<th:block th:each="reference, rowStat : *{references}">
<div class="row" th:id="|reference${rowStat.index}|"
th:style="${reference.deleted} ? 'display: none;' :''">
<div class="form-group col-11">
<a data-toggle="collapse"
th:href="${'#collapseReference'+rowStat.index}"
aria-expanded="false">
<div class="collapse-heading"
th:text="${(reference.referenceType.toString() == 'ARTICLE'?'Статья':'Книга') +'. '
+(reference.publicationTitle==null?'':reference.publicationTitle+'. ')+(reference.authors==null?'':reference.authors+'. ') }">
</div>
</a>
</div>
<div class="col-1">
<a class="btn btn-danger float-right"
th:onclick="|$('#references${rowStat.index}\\.deleted').val('true'); $('#reference${rowStat.index}').hide(); |"><span
aria-hidden="true"><i class="fa fa-times"/></span>
</a>
</div>
<div class="collapse" th:id="${'collapseReference'+rowStat.index}">
<input type="hidden"
th:field="*{references[__${rowStat.index}__].id}"/>
<input type="hidden"
th:field="*{references[__${rowStat.index}__].deleted}"/>
<div class="form-group col-12">
<label class="col-4">Вид публикации:</label>
<select class="form-control col-7"
th:field="*{references[__${rowStat.index}__].referenceType}">
<option th:each="type : ${allReferenceTypes}"
th:value="${type}"
th:text="${type.typeName}">Type
</option>
</select>
</div>
<div class="form-group col-12">
<label class="col-4">Авторы:</label>
<input type="text"
class="form-control col-7 autocomplete author"
name="authors"
th:field="*{references[__${rowStat.index}__].authors}"/>
</div>
<div class="form-group col-12">
<label class="col-4 ">Название публикации:</label>
<input type="text"
class="form-control col-7 autocomplete publicationTitle"
name="publicationTitle"
th:field="*{references[__${rowStat.index}__].publicationTitle}"/>
</div>
<div class="form-group col-12">
<label class="col-4">Год издания:</label>
<input type="number" class="form-control col-7 "
name="publicationYear"
th:field="*{references[__${rowStat.index}__].publicationYear}"/>
</div>
<div class="form-group col-12">
<label class="col-4">Издательство:</label>
<input type="text"
class="form-control col-7 autocomplete publisher"
name="publisher"
th:field="*{references[__${rowStat.index}__].publisher}"/>
</div>
<div class="form-group col-12">
<label class="col-4">Страницы:</label>
<input type="text" class="form-control col-7" name="pages"
th:field="*{references[__${rowStat.index}__].pages}"/>
</div>
<div class="form-group col-12">
<label class="col-4">Название журнала или сборника статей
конференции:</label>
<input type="text"
class="form-control col-7 autocomplete journalOrCollectionTitle"
name="journalOrCollectionTitle"
th:field="*{references[__${rowStat.index}__].journalOrCollectionTitle}"/>
</div>
</div>
</div>
</th:block>
<div class="form-group">
<input type="submit" id="addReference" name="addReference"
class="btn btn-primary"
value="Добавить источник"/>
</div>
<div class="form-group">
<div class="col-12 form-group">
<label for="formatStandard">Стандарт форматирования:</label>
<select class="form-control" th:field="*{formatStandard}"
id="formatStandard">
<option th:each="standard : ${allFormatStandards}"
th:value="${standard}"
th:text="${standard.standardName}">standard
</option>
</select>
</div>
<div class="col-12 form-group">
<button id="formatBtn" class="btn btn-primary text-uppercase"
onclick="getFormattedReferences()"
type="button">
Форматировать
</button>
</div>
<div class="col-12">
<textarea class="form-control"
id="formattedReferencesArea"></textarea>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="col-md-4 offset-md-1 col-sm-12 offset-sm-0"> <div class="col-md-4 offset-md-1 col-sm-12 offset-sm-0">
@ -379,6 +507,63 @@
} }
} }
function getFormattedReferences() {
var formData = new FormData(document.forms.paperform);
var xhr = new XMLHttpRequest();
xhr.open("POST", urlReferencesFormatting);
console.log(formData);
xhr.send(formData);
xhr.onload = function () {
if (this.status == 200) {
console.debug(this.response);
$('#formattedReferencesArea').val(this.response);
} else {
showFeedbackMessage("Ошибка при форматировании списка литературы", MessageTypesEnum.DANGER);
}
}
}
</script>
<script th:inline="javascript">
/*<![CDATA[*/
var autoCompleteData = /*[[${autocompleteData}]]*/ 'default';
console.log(autoCompleteData);
$(".autocomplete.author").autocomplete({
source: autoCompleteData.authors,
minLength: 0,
}).focus(function () {
$(this).autocomplete("search");
});
$(".autocomplete.publicationTitle").autocomplete({
source: autoCompleteData.publicationTitles,
minLength: 0,
}).focus(function () {
$(this).autocomplete("search");
});
$(".autocomplete.publisher").autocomplete({
source: autoCompleteData.publishers,
minLength: 0,
}).focus(function () {
$(this).autocomplete("search");
});
$(".autocomplete.journalOrCollectionTitle").autocomplete({
source: autoCompleteData.journalOrCollectionTitles,
minLength: 0,
}).focus(function () {
$(this).autocomplete("search");
});
/*]]>*/
</script> </script>
</div> </div>

View File

@ -1,57 +1,59 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default"> layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
<script src="/js/users.js"></script>
<link rel="stylesheet" href="../css/base.css"/>
</head> </head>
<body> <body>
<nav layout:fragment="navbar"> <nav layout:fragment="navbar">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand" href="/"><span class="ui-menuitem-text"><i <a class="navbar-brand" href="/"><span class="ui-menuitem-text"><i
class="fa fa-plane fa-4" aria-hidden="true"></i> Balance</span></a> class="fa fa-plane fa-4" aria-style="display:none"></i> Balance</span></a>
</div> </div>
</nav> </nav>
<div class="container" layout:fragment="content"> <div layout:fragment="content">
<form id="reset-form" method="post" class="margined-top-10"> <section class="bg-light" id="portfolio">
<fieldset>
<div class="form-group">
<input type="email" name="email" id="email" class="form-control"
placeholder="E-Mail" required="true" autofocus="autofocus"/>
</div>
<button type="submit" class="btn btn-success btn-block">Сбросить пароль</button>
<div class="form-group">
<small class="form-text text-muted">
<a href="/login">Вернуться к странице входа</a>
</small>
</div>
</fieldset>
</form>
</div>
<th:block layout:fragment="data-scripts">
<script type="text/javascript">
/*<![CDATA[*/
$(document).ready(function () {
$("#reset-form").submit(function () {
var email = $("#email").val();
if (isEmpty(email)) {
showFeedbackMessage("Адрес электронной почты не задан", MessageTypesEnum.DANGER);
return false;
}
postToRest(urlUsersPasswordResetRequest + "?email=" + email, null,
function () {
showFeedbackMessage("Запрос на смену пароля отправлен");
},
function () {
$("#email").val("");
}
);
return false;
});
});
/*]]>*/
</script> <div class="container">
</th:block> <div class="row justify-content-md-center">
<div class="col-lg-6">
<div class="form-group">
<input type="text" name="email" id="emailReset" class="form-control"
placeholder="E-Mail"/>
</div>
<div class="form-group">
<input type="password" name="email" id="passwordNew" class="form-control"
placeholder="Новый пароль" style="display:none"/>
</div>
<div class="form-group">
<input type="password" name="email" id="passwordConfirm" class="form-control"
placeholder="Подтвердите пароль" style="display:none"/>
</div>
<div class="form-group">
<input type="text" name="email" id="resetKey" class="form-control"
placeholder="Код подтверждения" style="display:none"/>
</div>
<div id="dvloader" class="loader" style="display:none"><img src="../img/main/ajax-loader.gif" /></div>
<button id="btnSend" type="button" onclick="requestResetPassword()"
class="btn btn-success btn-block">
Отправить код подтверждения
</button>
<button id="btnReset" style="display:none" type="button" onclick="resetPassword()"
class="btn btn-success btn-block">
Сбросить
пароль
</button>
<div class="form-group">
<small class="form-text text-muted">
<a href="/login">Вернуться к странице входа</a>
</small>
</div>
</div>
</div>
</div>
</section>
</div>
</body> </body>
</html> </html>

View File

@ -0,0 +1,216 @@
package ru.ulstu.students.service;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.domain.Sort;
import ru.ulstu.core.util.DateUtils;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.students.model.Scheduler;
import ru.ulstu.students.model.Task;
import ru.ulstu.students.model.TaskDto;
import ru.ulstu.students.model.TaskFilterDto;
import ru.ulstu.students.repository.SchedulerRepository;
import ru.ulstu.students.repository.TaskRepository;
import ru.ulstu.tags.model.Tag;
import ru.ulstu.tags.service.TagService;
import ru.ulstu.timeline.service.EventService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class TaskServiceTest {
@Mock
TaskRepository taskRepository;
@Mock
SchedulerRepository schedulerRepository;
@Mock
private TagService tagService;
@Mock
DeadlineService deadlineService;
@Mock
EventService eventService;
@InjectMocks
TaskService taskService;
private final static Sort SORT = new Sort(Sort.Direction.DESC, "createDate");
private final static Integer ID = 1;
private final static Task.TaskStatus STATUS = Task.TaskStatus.IN_WORK;
private final static String TITLE = "title";
private final static String DESCR = "descr";
private final static String TAG = "tag";
private final static Date YEAR = DateUtils.clearTime(DateUtils.addYears(new Date(), 0));
private List<Task> tasks;
private List<Tag> tags;
private Task task;
private Task taskForSchedule;
private TaskDto taskDto;
private Tag tag;
private Deadline deadline;
private List<Deadline> deadlines;
private Scheduler scheduler;
@Before
public void setUp() throws Exception {
tasks = new ArrayList<>();
task = new Task();
task.setId(ID);
task.setTitle(TITLE);
task.setDescription(DESCR);
tag = new Tag();
tag.setId(ID);
tag.setTagName(TAG);
deadlines = new ArrayList<>();
deadline = new Deadline(new Date(), DESCR);
deadline.setId(ID);
deadlines.add(deadline);
task.setDeadlines(deadlines);
tags = new ArrayList<>();
tags.add(tag);
tasks.add(task);
taskDto = new TaskDto(task);
taskForSchedule = new Task();
taskForSchedule.setTitle(TITLE);
taskForSchedule.setDescription(DESCR);
scheduler = new Scheduler();
scheduler.setDate(new Date());
scheduler.setTask(taskForSchedule);
}
@Test
public void findAll() {
when(taskRepository.findAll(SORT)).thenReturn(tasks);
assertEquals(Collections.singletonList(task), taskService.findAll());
}
@Test
public void filter() {
when(tagService.findById(ID)).thenReturn(tag);
when(taskRepository.filterNew(STATUS, tag)).thenReturn(tasks);
TaskFilterDto taskFilterDto = new TaskFilterDto();
taskFilterDto.setTag(ID);
taskFilterDto.setOrder("new");
taskFilterDto.setStatus(STATUS);
assertEquals(Collections.singletonList(taskDto), taskService.filter(taskFilterDto));
}
@Test
public void create() throws IOException {
when(tagService.saveOrCreate(new ArrayList<>())).thenReturn(tags);
when(deadlineService.saveOrCreate(new ArrayList<>())).thenReturn(deadlines);
when(taskRepository.save(new Task())).thenReturn(task);
eventService.createFromTask(new Task());
taskDto.setTags(tags);
taskDto.setDeadlines(deadlines);
Task newTask = new Task();
task.setId(ID);
task.setTitle(TITLE);
task.setDescription(DESCR);
task.setTags(tags);
task.setDeadlines(deadlines);
assertEquals(task.getId(), taskService.create(taskDto));
}
@Test
public void delete() throws IOException {
when(taskRepository.exists(ID)).thenReturn(true);
when(taskRepository.findOne(ID)).thenReturn(task);
when(schedulerRepository.findOneByTask(task)).thenReturn(null);
assertTrue(taskService.delete(ID));
}
@Test
public void generateYearTasks() {
when(tagService.getTags()).thenReturn(tags);
tasks.get(0).setTags(tags);
when(taskRepository.findAllYear(DateUtils.clearTime(DateUtils.addYears(new Date(), -1)))).thenReturn(tasks);
tasks.get(0).setCreateDate(DateUtils.clearTime(DateUtils.addYears(new Date(), -1)));
when(taskRepository.findByTag(tag)).thenReturn(tasks);
Task newTask = new Task();
newTask.setTitle(tasks.get(0).getTitle());
newTask.setTags(tasks.get(0).getTags());
newTask.setCreateDate(new Date());
newTask.setStatus(Task.TaskStatus.LOADED_FROM_KIAS);
Deadline newDeadline = new Deadline();
newDeadline.setId(ID);
newDeadline.setDescription(deadline.getDescription());
newDeadline.setDate(DateUtils.addYears(deadline.getDate(), 1));
when(deadlineService.create(newDeadline)).thenReturn(newDeadline);
newTask.setDeadlines(Arrays.asList(newDeadline));
when(taskRepository.save(newTask)).thenReturn(task);
assertEquals(Arrays.asList(task), taskService.generateYearTasks());
}
@Test
public void checkRepeatingTags() {
when(tagService.getTags()).thenReturn(tags);
tasks.get(0).setTags(tags);
when(taskRepository.findAllYear(DateUtils.clearTime(DateUtils.addYears(new Date(), -1)))).thenReturn(tasks);
assertEquals(new HashSet<Tag>(Arrays.asList(tag)), taskService.checkRepeatingTags(false));
}
@Test
public void createPeriodTask() {
Task newTask = new Task();
newTask.setTitle(scheduler.getTask().getTitle());
newTask.setTags(scheduler.getTask().getTags());
newTask.setCreateDate(new Date());
Deadline newDeadline = new Deadline();
newDeadline.setId(ID);
newDeadline.setDescription(deadline.getDescription());
newDeadline.setDate(DateUtils.addYears(deadline.getDate(), 1));
when(deadlineService.create(newDeadline)).thenReturn(newDeadline);
newTask.setDeadlines(Arrays.asList(newDeadline));
when(taskRepository.save(newTask)).thenReturn(taskForSchedule);
assertEquals(taskForSchedule, taskService.createPeriodTask(scheduler));
}
}