From bf5b800f7d7529343b9d3d4a5126d5d7ecacc95b Mon Sep 17 00:00:00 2001 From: neviyn Date: Fri, 2 Apr 2021 20:36:16 +0100 Subject: [PATCH] Can now comment on events --- .../uk/co/neviyn/projectplanner/Entities.kt | 10 +- .../neviyn/projectplanner/HtmlController.kt | 24 ++++- .../uk/co/neviyn/projectplanner/Requests.kt | 6 +- .../db/migration/V1_0__Database_Init.sql | 14 +-- src/main/resources/static/js/project.js | 54 ++++++++++- src/main/resources/templates/project.html | 95 +++++++++++++------ 6 files changed, 156 insertions(+), 47 deletions(-) diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt index 80702b7..1e602a1 100644 --- a/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt +++ b/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt @@ -2,7 +2,6 @@ package uk.co.neviyn.projectplanner import com.fasterxml.jackson.annotation.JsonIgnore import java.time.Instant -import java.time.LocalDateTime import javax.persistence.CascadeType import javax.persistence.Column import javax.persistence.Entity @@ -69,6 +68,8 @@ class Event( @ManyToMany(mappedBy = "events") @JsonIgnore var tags: MutableSet = mutableSetOf(), + @OneToMany(mappedBy = "event") + var comments: MutableList = mutableListOf(), @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null ) @@ -82,12 +83,13 @@ class Comment( @JsonIgnore var event: Event? = null, @ManyToMany(mappedBy = "comments") - @JsonIgnore var tags: MutableSet = mutableSetOf(), - var created: LocalDateTime = LocalDateTime.MIN, + var created: Instant = Instant.MIN, var comment: String = "INVALID", @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null -) +) { + fun toFlatComment(): FlatComment = FlatComment(created, comment, user!!.username) +} @Entity class Tag( diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt index 8f8a813..3903690 100644 --- a/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt +++ b/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt @@ -1,6 +1,7 @@ package uk.co.neviyn.projectplanner import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.HttpStatus import org.springframework.security.access.prepost.PreAuthorize import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.stereotype.Controller @@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.ResponseBody +import org.springframework.web.server.ResponseStatusException import java.time.Instant import javax.persistence.EntityManager import javax.transaction.Transactional @@ -95,11 +97,11 @@ class HtmlController @Autowired constructor(val userRepository: UserRepository, @Controller @RequestMapping("/project/{id}") -class ProjectController @Autowired constructor(val projectRepository: ProjectRepository, val userRepository: UserRepository, val eventRepository: EventRepository) { +class ProjectController @Autowired constructor(val projectRepository: ProjectRepository, val userRepository: UserRepository, val eventRepository: EventRepository, val commentRepository: CommentRepository) { @GetMapping("") @PreAuthorize("hasPermission(#id, 'Long', '')") - fun getProject(@PathVariable id: Long, model: Model) : String { + fun getProject(@PathVariable id: Long, model: Model): String { val project = projectRepository.findById(id).get() val nonMembers = userRepository.findByIdNotIn(project.members.map { it.id!! }) model.addAttribute("project", project) @@ -169,5 +171,23 @@ class ProjectController @Autowired constructor(val projectRepository: ProjectRep eventRepository.deleteById(e.id) } + @GetMapping("/eventcomments/{eventID}") + @PreAuthorize("hasPermission(#id, 'Long', '')") + @ResponseBody + fun getCommentsForEvent(@PathVariable id: Long, @PathVariable eventID: Long): List { + val project = projectRepository.findById(id).get() + val event = eventRepository.findById(eventID).get() + if (event.project != project) throw ResponseStatusException(HttpStatus.FORBIDDEN, "This comment does not belong to this project") + return event.comments.map { it.toFlatComment() } + } + @PostMapping("/addcomment/{eventID}") + @PreAuthorize("hasPermission(#id, 'Long', '')") + @ResponseBody + fun addCommentToEvent(@PathVariable id: Long, @PathVariable eventID: Long, @RequestBody c: NewComment, @AuthenticationPrincipal userDetails: CustomUserDetails) { + val event = eventRepository.findById(eventID).get() + val comment = Comment(user = userDetails.user, event = event, created = Instant.now(), comment = c.comment) + event.comments.add(comment) + commentRepository.save(comment) + } } \ No newline at end of file diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt index c22a89a..b6f9527 100644 --- a/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt +++ b/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt @@ -10,4 +10,8 @@ data class NewEvent(val title: String, val description: String, val start: Insta data class EditedEvent(val id: Long, val title: String, val description: String, val start: Instant, val end: Instant) -data class EventID(val id: Long) \ No newline at end of file +data class EventID(val id: Long) + +data class FlatComment(val created: Instant, val comment: String, val username: String) + +data class NewComment(val comment: String) \ No newline at end of file diff --git a/src/main/resources/db/migration/V1_0__Database_Init.sql b/src/main/resources/db/migration/V1_0__Database_Init.sql index 2899d1c..3213006 100644 --- a/src/main/resources/db/migration/V1_0__Database_Init.sql +++ b/src/main/resources/db/migration/V1_0__Database_Init.sql @@ -53,17 +53,17 @@ create unique index if not exists event_id_uindex create table if not exists projectplanner.comment ( - id bigserial not null + id bigserial not null constraint comment_pk - primary key + primary key, + event_id bigserial not null constraint comment_event_fk - references projectplanner.event + references projectplanner.event, + user_id bigserial not null constraint comment_user_fk references projectplanner."user", - event_id bigserial not null, - user_id bigserial not null, - created timestamp not null, - comment text not null + created timestamp not null, + comment text not null ); create unique index if not exists comment_id_uindex diff --git a/src/main/resources/static/js/project.js b/src/main/resources/static/js/project.js index c8f998b..68666ee 100644 --- a/src/main/resources/static/js/project.js +++ b/src/main/resources/static/js/project.js @@ -1,6 +1,9 @@ +let DateTime = luxon.DateTime; let calendar; let addModal; let editModal; +let memberList; +let commentList; window.onload = function () { flatpickr("#startTimeInput", {enableTime: true}) flatpickr("#endTimeInput", {enableTime: true}) @@ -71,15 +74,24 @@ window.onload = function () { document.getElementById('descriptionEdit').value = eventInfo.event.extendedProps.description startTimeEdit.setDate(eventInfo.event.start) endTimeEdit.setDate(eventInfo.event.end) + setComments(eventInfo.event.id) editModal.show() }, events: window.location.origin + window.location.pathname + "/events" }); calendar.render(); - const options = { - valueNames: ['username'], - }; - new List('user-list', options); + try { + const options = { + valueNames: ['username'], + }; + memberList = new List('user-list', options); + } catch (e) { + } + const commentOptions = { + valueNames: ['comment', 'created', 'username'], + item: '

|
' + } + commentList = new List('comment-list', commentOptions) }; function addEvent() { @@ -101,4 +113,38 @@ function editEvent() { if (myEvent.title !== title) myEvent.setProp('title', title) if (myEvent.description !== description) myEvent.setExtendedProp('description', description) myEvent.setDates(start, end) +} + +async function setComments(eventID) { + let data = await fetch(window.location.origin + window.location.pathname + "/eventcomments/" + eventID, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=utf-8', + 'X-CSRF-TOKEN': csrf_token + } + }).then(x => { + return x.json() + }) + commentList.clear() + data.forEach(function (x) { + x.created = DateTime.fromISO(x.created).toLocaleString(DateTime.DATETIME_FULL) + commentList.add(x) + }) +} + +function saveComment() { + let id = document.getElementById('idEdit').value + let newComment = { + comment: document.getElementById('addComment').value + } + fetch(window.location.origin + window.location.pathname + "/addcomment/" + id, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=utf-8', + 'X-CSRF-TOKEN': csrf_token + }, + body: JSON.stringify(newComment) + }) + // Refresh comments after adding one + setComments(id) } \ No newline at end of file diff --git a/src/main/resources/templates/project.html b/src/main/resources/templates/project.html index fc1e3af..81882fe 100644 --- a/src/main/resources/templates/project.html +++ b/src/main/resources/templates/project.html @@ -4,15 +4,21 @@ Project Planner + + - + @@ -91,7 +97,7 @@