src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt

changeset 311
bf67e0ff7131
equal deleted inserted replaced
310:bbf4eb9a71f8 311:bf67e0ff7131
1 package de.uapcore.lightpit.logic
2
3 import de.uapcore.lightpit.HttpRequest
4 import de.uapcore.lightpit.dao.DataAccessObject
5 import de.uapcore.lightpit.dateOptValidator
6 import de.uapcore.lightpit.entities.Issue
7 import de.uapcore.lightpit.entities.IssueComment
8 import de.uapcore.lightpit.entities.IssueRelation
9 import de.uapcore.lightpit.entities.Version
10 import de.uapcore.lightpit.types.IssueCategory
11 import de.uapcore.lightpit.types.IssueStatus
12 import de.uapcore.lightpit.types.RelationType
13 import de.uapcore.lightpit.viewmodel.IssueDetailView
14 import de.uapcore.lightpit.viewmodel.PathInfos
15 import de.uapcore.lightpit.viewmodel.PathInfosFull
16 import de.uapcore.lightpit.viewmodel.projectNavMenu
17 import java.sql.Date
18
19 fun Issue.hasChanged(reference: Issue) = !(component == reference.component &&
20 status == reference.status &&
21 category == reference.category &&
22 subject == reference.subject &&
23 description == reference.description &&
24 assignee == reference.assignee &&
25 eta == reference.eta &&
26 affected == reference.affected &&
27 resolved == reference.resolved)
28
29 fun Issue.compareEtaTo(date: Date?): Int {
30 val eta = this.eta
31 return if (eta == null && date == null) 0
32 else if (eta == null) 1
33 else if (date == null) -1
34 else eta.compareTo(date)
35 }
36
37 fun Issue.applyFormData(http: HttpRequest, dao: DataAccessObject): Issue = this.apply {
38 component = dao.findComponent(http.param("component")?.toIntOrNull() ?: -1)
39 category = IssueCategory.valueOf(http.param("category") ?: "")
40 status = IssueStatus.valueOf(http.param("status") ?: "")
41 subject = http.param("subject") ?: ""
42 description = http.param("description") ?: ""
43 assignee = http.param("assignee")?.toIntOrNull()?.let {
44 when (it) {
45 -1 -> null
46 -2 -> (component?.lead ?: project.owner)
47 else -> dao.findUser(it)
48 }
49 }
50 // TODO: process error messages
51 eta = http.param("eta", ::dateOptValidator, null, mutableListOf())
52
53 affected = http.param("affected")?.toIntOrNull()?.takeIf { it > 0 }?.let { Version(it, project.id) }
54 resolved = http.param("resolved")?.toIntOrNull()?.takeIf { it > 0 }?.let { Version(it, project.id) }
55 }
56
57 fun processIssueForm(issue: Issue, reference: Issue, http: HttpRequest, dao: DataAccessObject) {
58 if (issue.hasChanged(reference)) {
59 dao.updateIssue(issue)
60 dao.insertHistoryEvent(issue)
61 }
62 val newComment = http.param("comment")
63 if (!newComment.isNullOrBlank()) {
64 val comment = IssueComment(-1, issue.id).apply {
65 author = http.remoteUser?.let { dao.findUserByName(it) }
66 comment = newComment
67 }
68 val commentid = dao.insertComment(comment)
69 dao.insertHistoryEvent(issue, comment, commentid)
70 }
71 }
72
73 fun commitIssueComment(http: HttpRequest, dao: DataAccessObject, pathInfos: PathInfos) {
74 val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
75 if (issue == null) {
76 http.response.sendError(404)
77 return
78 }
79 if (processIssueComment(issue, http, dao)) {
80 http.renderCommit("${pathInfos.issuesHref}${issue.id}")
81 }
82 }
83
84 fun processIssueComment(issue:Issue, http: HttpRequest, dao: DataAccessObject): Boolean {
85 val commentId = http.param("commentid")?.toIntOrNull() ?: -1
86 if (commentId > 0) {
87 val comment = dao.findComment(commentId)
88 if (comment == null) {
89 http.response.sendError(404)
90 return false
91 }
92 val originalAuthor = comment.author?.username
93 if (originalAuthor != null && originalAuthor == http.remoteUser) {
94 val newComment = http.param("comment")
95 if (!newComment.isNullOrBlank()) {
96 comment.comment = newComment
97 dao.updateComment(comment)
98 dao.insertHistoryEvent(issue, comment)
99 }
100 } else {
101 http.response.sendError(403)
102 return false
103 }
104 } else {
105 val comment = IssueComment(-1, issue.id).apply {
106 author = http.remoteUser?.let { dao.findUserByName(it) }
107 comment = http.param("comment") ?: ""
108 }
109 val newId = dao.insertComment(comment)
110 dao.insertHistoryEvent(issue, comment, newId)
111 }
112 return true
113 }
114
115 fun renderIssueView(
116 http: HttpRequest,
117 dao: DataAccessObject,
118 issue: Issue,
119 pathInfos: PathInfos,
120 relationError: String? = null
121 ) {
122 val comments = dao.listComments(issue)
123
124 with(http) {
125 pageTitle = "#${issue.id} ${issue.subject} (${issue.project.name})"
126 view = IssueDetailView(
127 issue,
128 comments,
129 dao.listIssues(issue.project, true),
130 dao.listIssueRelations(issue),
131 dao.listCommitRefs(issue),
132 relationError,
133 pathInfos
134 )
135 if (pathInfos is PathInfosFull) {
136 navigationMenu = projectNavMenu(dao.listProjects(), pathInfos)
137 }
138 styleSheets = listOf("projects")
139 javascript = "issue-editor"
140 render("issue-view")
141 }
142 }
143
144 fun addIssueRelation(http: HttpRequest, dao: DataAccessObject, pathInfos: PathInfos) {
145 val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
146 if (issue == null) {
147 http.response.sendError(404)
148 return
149 }
150
151 // determine the relation type
152 val type: Pair<RelationType, Boolean>? = http.param("type")?.let {
153 try {
154 if (it.startsWith("!")) {
155 Pair(RelationType.valueOf(it.substring(1)), true)
156 } else {
157 Pair(RelationType.valueOf(it), false)
158 }
159 } catch (_: IllegalArgumentException) {
160 null
161 }
162 }
163
164 // if the relation type was invalid, send HTTP 500
165 if (type == null) {
166 http.response.sendError(500)
167 return
168 }
169
170 // determine the target issue
171 val targetIssue: Issue? = http.param("issue")?.let {
172 if (it.startsWith("#") && it.length > 1) {
173 it.substring(1).split(" ", limit = 2)[0].toIntOrNull()
174 ?.let(dao::findIssue)
175 ?.takeIf { target -> target.project.id == issue.project.id }
176 } else {
177 null
178 }
179 }
180
181 // check if the target issue is valid
182 if (targetIssue == null) {
183 renderIssueView(http, dao, issue, pathInfos, "issue.relations.target.invalid")
184 return
185 }
186
187 // commit the result
188 dao.insertIssueRelation(IssueRelation(issue, targetIssue, type.first, type.second))
189 http.renderCommit("${pathInfos.issuesHref}${issue.id}")
190 }
191
192 fun removeIssueRelation(http: HttpRequest, dao: DataAccessObject, pathInfos: PathInfos) {
193 val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
194 if (issue == null) {
195 http.response.sendError(404)
196 return
197 }
198
199 // determine relation
200 val type = http.param("type")?.let {
201 try {
202 RelationType.valueOf(it)}
203 catch (_:IllegalArgumentException) {null}
204 }
205 if (type == null) {
206 http.response.sendError(500)
207 return
208 }
209 val rel = http.param("to")?.toIntOrNull()?.let(dao::findIssue)?.let {
210 IssueRelation(
211 issue,
212 it,
213 type,
214 http.param("reverse")?.toBoolean() ?: false
215 )
216 }
217
218 // execute removal, if there is something to remove
219 rel?.run(dao::deleteIssueRelation)
220
221 // always pretend that the operation was successful - if there was nothing to remove, it's okay
222 http.renderCommit("${pathInfos.issuesHref}${issue.id}")
223 }

mercurial