Sun, 10 Oct 2021 15:12:12 +0200
fix diff generated between feed entries with different issue IDs
184 | 1 | /* |
2 | * Copyright 2021 Mike Becker. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions are met: | |
6 | * | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * | |
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
20 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
21 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
22 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | package de.uapcore.lightpit.servlet | |
27 | ||
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
28 | import de.uapcore.lightpit.* |
184 | 29 | import de.uapcore.lightpit.dao.DataAccessObject |
30 | import de.uapcore.lightpit.entities.* | |
31 | import de.uapcore.lightpit.types.IssueCategory | |
32 | import de.uapcore.lightpit.types.IssueStatus | |
33 | import de.uapcore.lightpit.types.VersionStatus | |
34 | import de.uapcore.lightpit.types.WebColor | |
35 | import de.uapcore.lightpit.util.AllFilter | |
36 | import de.uapcore.lightpit.util.IssueFilter | |
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
37 | import de.uapcore.lightpit.util.IssueSorter.Companion.DEFAULT_ISSUE_SORTER |
184 | 38 | import de.uapcore.lightpit.util.SpecificFilter |
39 | import de.uapcore.lightpit.viewmodel.* | |
40 | import java.sql.Date | |
41 | import javax.servlet.annotation.WebServlet | |
42 | ||
43 | @WebServlet(urlPatterns = ["/projects/*"]) | |
44 | class ProjectServlet : AbstractServlet() { | |
45 | ||
46 | init { | |
47 | get("/", this::projects) | |
48 | get("/%project", this::project) | |
49 | get("/%project/issues/%version/%component/", this::project) | |
50 | get("/%project/edit", this::projectForm) | |
51 | get("/-/create", this::projectForm) | |
52 | post("/-/commit", this::projectCommit) | |
53 | ||
54 | get("/%project/versions/", this::versions) | |
55 | get("/%project/versions/%version/edit", this::versionForm) | |
56 | get("/%project/versions/-/create", this::versionForm) | |
57 | post("/%project/versions/-/commit", this::versionCommit) | |
58 | ||
59 | get("/%project/components/", this::components) | |
60 | get("/%project/components/%component/edit", this::componentForm) | |
61 | get("/%project/components/-/create", this::componentForm) | |
62 | post("/%project/components/-/commit", this::componentCommit) | |
63 | ||
64 | get("/%project/issues/%version/%component/%issue", this::issue) | |
65 | get("/%project/issues/%version/%component/%issue/edit", this::issueForm) | |
186
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
66 | post("/%project/issues/%version/%component/%issue/comment", this::issueComment) |
184 | 67 | get("/%project/issues/%version/%component/-/create", this::issueForm) |
186
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
68 | post("/%project/issues/%version/%component/-/commit", this::issueCommit) |
184 | 69 | } |
70 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
71 | private fun projects(http: HttpRequest, dao: DataAccessObject) { |
184 | 72 | val projects = dao.listProjects() |
73 | val projectInfos = projects.map { | |
74 | ProjectInfo( | |
75 | project = it, | |
76 | versions = dao.listVersions(it), | |
77 | components = emptyList(), // not required in this view | |
78 | issueSummary = dao.collectIssueSummary(it) | |
79 | ) | |
80 | } | |
81 | ||
82 | with(http) { | |
83 | view = ProjectsView(projectInfos) | |
84 | navigationMenu = projectNavMenu(projects) | |
85 | styleSheets = listOf("projects") | |
86 | render("projects") | |
87 | } | |
88 | } | |
89 | ||
90 | private fun activeProjectNavMenu( | |
91 | projects: List<Project>, | |
92 | projectInfo: ProjectInfo, | |
93 | selectedVersion: Version? = null, | |
94 | selectedComponent: Component? = null | |
95 | ) = | |
96 | projectNavMenu( | |
97 | projects, | |
98 | projectInfo.versions, | |
99 | projectInfo.components, | |
100 | projectInfo.project, | |
101 | selectedVersion, | |
102 | selectedComponent | |
103 | ) | |
104 | ||
210 | 105 | private sealed interface LookupResult<T> |
106 | private class NotFound<T> : LookupResult<T> | |
107 | private data class Found<T>(val elem: T?) : LookupResult<T> | |
184 | 108 | |
109 | private fun <T : HasNode> HttpRequest.lookupPathParam(paramName: String, list: List<T>): LookupResult<T> { | |
110 | val node = pathParams[paramName] | |
111 | return if (node == null || node == "-") { | |
210 | 112 | Found(null) |
184 | 113 | } else { |
114 | val result = list.find { it.node == node } | |
115 | if (result == null) { | |
210 | 116 | NotFound() |
184 | 117 | } else { |
210 | 118 | Found(result) |
184 | 119 | } |
120 | } | |
121 | } | |
122 | ||
123 | private fun obtainProjectInfo(http: HttpRequest, dao: DataAccessObject): ProjectInfo? { | |
124 | val project = dao.findProjectByNode(http.pathParams["project"] ?: "") ?: return null | |
125 | ||
126 | val versions: List<Version> = dao.listVersions(project) | |
127 | val components: List<Component> = dao.listComponents(project) | |
128 | ||
129 | return ProjectInfo( | |
130 | project, | |
131 | versions, | |
132 | components, | |
133 | dao.collectIssueSummary(project) | |
134 | ) | |
135 | } | |
136 | ||
137 | private fun sanitizeNode(name: String): String { | |
138 | val san = name.replace(Regex("[/\\\\]"), "-") | |
139 | return if (san.startsWith(".")) { | |
140 | "v$san" | |
141 | } else { | |
142 | san | |
143 | } | |
144 | } | |
145 | ||
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
146 | private fun feedPath(project: Project) = "feed/${project.node}/issues.rss" |
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
147 | |
210 | 148 | private data class PathInfos( |
184 | 149 | val projectInfo: ProjectInfo, |
150 | val version: Version?, | |
151 | val component: Component? | |
152 | ) { | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
153 | val project = projectInfo.project |
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
154 | val issuesHref by lazyOf("projects/${project.node}/issues/${version?.node ?: "-"}/${component?.node ?: "-"}/") |
184 | 155 | } |
156 | ||
157 | private fun withPathInfo(http: HttpRequest, dao: DataAccessObject): PathInfos? { | |
158 | val projectInfo = obtainProjectInfo(http, dao) | |
159 | if (projectInfo == null) { | |
160 | http.response.sendError(404) | |
161 | return null | |
162 | } | |
163 | ||
164 | val version = when (val result = http.lookupPathParam("version", projectInfo.versions)) { | |
210 | 165 | is NotFound -> { |
184 | 166 | http.response.sendError(404) |
167 | return null | |
168 | } | |
210 | 169 | is Found -> { |
184 | 170 | result.elem |
171 | } | |
172 | } | |
173 | val component = when (val result = http.lookupPathParam("component", projectInfo.components)) { | |
210 | 174 | is NotFound -> { |
184 | 175 | http.response.sendError(404) |
176 | return null | |
177 | } | |
210 | 178 | is Found -> { |
184 | 179 | result.elem |
180 | } | |
181 | } | |
182 | ||
183 | return PathInfos(projectInfo, version, component) | |
184 | } | |
185 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
186 | private fun project(http: HttpRequest, dao: DataAccessObject) { |
184 | 187 | withPathInfo(http, dao)?.run { |
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
188 | |
184 | 189 | val issues = dao.listIssues(IssueFilter( |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
190 | project = SpecificFilter(project), |
184 | 191 | version = version?.let { SpecificFilter(it) } ?: AllFilter(), |
192 | component = component?.let { SpecificFilter(it) } ?: AllFilter() | |
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
193 | )).sortedWith(DEFAULT_ISSUE_SORTER) |
184 | 194 | |
195 | with(http) { | |
205
7725a79416f3
#115 adds custom page titles
Mike Becker <universe@uap-core.de>
parents:
200
diff
changeset
|
196 | pageTitle = project.name |
184 | 197 | view = ProjectDetails(projectInfo, issues, version, component) |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
198 | feedPath = feedPath(project) |
184 | 199 | navigationMenu = activeProjectNavMenu( |
200 | dao.listProjects(), | |
201 | projectInfo, | |
202 | version, | |
203 | component | |
204 | ) | |
205 | styleSheets = listOf("projects") | |
206 | render("project-details") | |
207 | } | |
208 | } | |
209 | } | |
210 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
211 | private fun projectForm(http: HttpRequest, dao: DataAccessObject) { |
200
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
212 | if (!http.pathParams.containsKey("project")) { |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
213 | http.view = ProjectEditView(Project(-1), dao.listUsers()) |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
214 | http.navigationMenu = projectNavMenu(dao.listProjects()) |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
215 | } else { |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
216 | val projectInfo = obtainProjectInfo(http, dao) |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
217 | if (projectInfo == null) { |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
218 | http.response.sendError(404) |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
219 | return |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
220 | } |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
221 | http.view = ProjectEditView(projectInfo.project, dao.listUsers()) |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
222 | http.navigationMenu = activeProjectNavMenu( |
184 | 223 | dao.listProjects(), |
224 | projectInfo | |
225 | ) | |
226 | } | |
200
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
227 | http.styleSheets = listOf("projects") |
a5ddfaf6b469
fixes project creation not working
Mike Becker <universe@uap-core.de>
parents:
198
diff
changeset
|
228 | http.render("project-form") |
184 | 229 | } |
230 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
231 | private fun projectCommit(http: HttpRequest, dao: DataAccessObject) { |
184 | 232 | val project = Project(http.param("id")?.toIntOrNull() ?: -1).apply { |
233 | name = http.param("name") ?: "" | |
234 | node = http.param("node") ?: "" | |
235 | description = http.param("description") ?: "" | |
236 | ordinal = http.param("ordinal")?.toIntOrNull() ?: 0 | |
237 | repoUrl = http.param("repoUrl") ?: "" | |
238 | owner = (http.param("owner")?.toIntOrNull() ?: -1).let { | |
239 | if (it < 0) null else dao.findUser(it) | |
240 | } | |
241 | // intentional defaults | |
242 | if (node.isBlank()) node = name | |
243 | // sanitizing | |
244 | node = sanitizeNode(node) | |
245 | } | |
246 | ||
247 | if (project.id < 0) { | |
248 | dao.insertProject(project) | |
249 | } else { | |
250 | dao.updateProject(project) | |
251 | } | |
252 | ||
253 | http.renderCommit("projects/${project.node}") | |
254 | } | |
255 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
256 | private fun versions(http: HttpRequest, dao: DataAccessObject) { |
184 | 257 | val projectInfo = obtainProjectInfo(http, dao) |
258 | if (projectInfo == null) { | |
259 | http.response.sendError(404) | |
260 | return | |
261 | } | |
262 | ||
263 | with(http) { | |
205
7725a79416f3
#115 adds custom page titles
Mike Becker <universe@uap-core.de>
parents:
200
diff
changeset
|
264 | pageTitle = "${projectInfo.project.name} - ${i18n("navmenu.versions")}" |
184 | 265 | view = VersionsView( |
266 | projectInfo, | |
267 | dao.listVersionSummaries(projectInfo.project) | |
268 | ) | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
269 | feedPath = feedPath(projectInfo.project) |
184 | 270 | navigationMenu = activeProjectNavMenu( |
271 | dao.listProjects(), | |
272 | projectInfo | |
273 | ) | |
274 | styleSheets = listOf("projects") | |
275 | render("versions") | |
276 | } | |
277 | } | |
278 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
279 | private fun versionForm(http: HttpRequest, dao: DataAccessObject) { |
184 | 280 | val projectInfo = obtainProjectInfo(http, dao) |
281 | if (projectInfo == null) { | |
282 | http.response.sendError(404) | |
283 | return | |
284 | } | |
285 | ||
286 | val version: Version | |
287 | when (val result = http.lookupPathParam("version", projectInfo.versions)) { | |
210 | 288 | is NotFound -> { |
184 | 289 | http.response.sendError(404) |
290 | return | |
291 | } | |
210 | 292 | is Found -> { |
184 | 293 | version = result.elem ?: Version(-1, projectInfo.project.id) |
294 | } | |
295 | } | |
296 | ||
297 | with(http) { | |
298 | view = VersionEditView(projectInfo, version) | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
299 | feedPath = feedPath(projectInfo.project) |
184 | 300 | navigationMenu = activeProjectNavMenu( |
301 | dao.listProjects(), | |
302 | projectInfo, | |
303 | selectedVersion = version | |
304 | ) | |
305 | styleSheets = listOf("projects") | |
306 | render("version-form") | |
307 | } | |
308 | } | |
309 | ||
210 | 310 | private fun obtainIdAndProject(http: HttpRequest, dao:DataAccessObject): Pair<Int, Project>? { |
184 | 311 | val id = http.param("id")?.toIntOrNull() |
312 | val projectid = http.param("projectid")?.toIntOrNull() ?: -1 | |
313 | val project = dao.findProject(projectid) | |
210 | 314 | return if (id == null || project == null) { |
184 | 315 | http.response.sendError(400) |
210 | 316 | null |
317 | } else { | |
318 | Pair(id, project) | |
184 | 319 | } |
210 | 320 | } |
184 | 321 | |
210 | 322 | private fun versionCommit(http: HttpRequest, dao: DataAccessObject) { |
323 | val idParams = obtainIdAndProject(http, dao) ?: return | |
324 | val (id, project) = idParams | |
325 | ||
326 | val version = Version(id, project.id).apply { | |
184 | 327 | name = http.param("name") ?: "" |
328 | node = http.param("node") ?: "" | |
329 | ordinal = http.param("ordinal")?.toIntOrNull() ?: 0 | |
330 | status = http.param("status")?.let(VersionStatus::valueOf) ?: VersionStatus.Future | |
225
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
331 | // TODO: process error messages |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
332 | eol = http.param("eol", ::dateOptValidator, null, mutableListOf()) |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
333 | release = http.param("release", ::dateOptValidator, null, mutableListOf()) |
184 | 334 | // intentional defaults |
335 | if (node.isBlank()) node = name | |
336 | // sanitizing | |
337 | node = sanitizeNode(node) | |
338 | } | |
339 | ||
225
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
340 | // sanitize eol and release date |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
341 | if (version.status.isEndOfLife) { |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
342 | if (version.eol == null) version.eol = Date(System.currentTimeMillis()) |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
343 | } else if (version.status.isReleased) { |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
344 | if (version.release == null) version.release = Date(System.currentTimeMillis()) |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
345 | } |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
346 | |
184 | 347 | if (id < 0) { |
348 | dao.insertVersion(version) | |
349 | } else { | |
350 | dao.updateVersion(version) | |
351 | } | |
352 | ||
353 | http.renderCommit("projects/${project.node}/versions/") | |
354 | } | |
355 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
356 | private fun components(http: HttpRequest, dao: DataAccessObject) { |
184 | 357 | val projectInfo = obtainProjectInfo(http, dao) |
358 | if (projectInfo == null) { | |
359 | http.response.sendError(404) | |
360 | return | |
361 | } | |
362 | ||
363 | with(http) { | |
205
7725a79416f3
#115 adds custom page titles
Mike Becker <universe@uap-core.de>
parents:
200
diff
changeset
|
364 | pageTitle = "${projectInfo.project.name} - ${i18n("navmenu.components")}" |
184 | 365 | view = ComponentsView( |
366 | projectInfo, | |
367 | dao.listComponentSummaries(projectInfo.project) | |
368 | ) | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
369 | feedPath = feedPath(projectInfo.project) |
184 | 370 | navigationMenu = activeProjectNavMenu( |
371 | dao.listProjects(), | |
372 | projectInfo | |
373 | ) | |
374 | styleSheets = listOf("projects") | |
375 | render("components") | |
376 | } | |
377 | } | |
378 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
379 | private fun componentForm(http: HttpRequest, dao: DataAccessObject) { |
184 | 380 | val projectInfo = obtainProjectInfo(http, dao) |
381 | if (projectInfo == null) { | |
382 | http.response.sendError(404) | |
383 | return | |
384 | } | |
385 | ||
386 | val component: Component | |
387 | when (val result = http.lookupPathParam("component", projectInfo.components)) { | |
210 | 388 | is NotFound -> { |
184 | 389 | http.response.sendError(404) |
390 | return | |
391 | } | |
210 | 392 | is Found -> { |
184 | 393 | component = result.elem ?: Component(-1, projectInfo.project.id) |
394 | } | |
395 | } | |
396 | ||
397 | with(http) { | |
398 | view = ComponentEditView(projectInfo, component, dao.listUsers()) | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
399 | feedPath = feedPath(projectInfo.project) |
184 | 400 | navigationMenu = activeProjectNavMenu( |
401 | dao.listProjects(), | |
402 | projectInfo, | |
403 | selectedComponent = component | |
404 | ) | |
405 | styleSheets = listOf("projects") | |
406 | render("component-form") | |
407 | } | |
408 | } | |
409 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
410 | private fun componentCommit(http: HttpRequest, dao: DataAccessObject) { |
210 | 411 | val idParams = obtainIdAndProject(http, dao) ?: return |
412 | val (id, project) = idParams | |
184 | 413 | |
210 | 414 | val component = Component(id, project.id).apply { |
184 | 415 | name = http.param("name") ?: "" |
416 | node = http.param("node") ?: "" | |
417 | ordinal = http.param("ordinal")?.toIntOrNull() ?: 0 | |
418 | color = WebColor(http.param("color") ?: "#000000") | |
419 | description = http.param("description") | |
227
f0ede8046b59
#162 adds active flag to component
Mike Becker <universe@uap-core.de>
parents:
225
diff
changeset
|
420 | // TODO: process error message |
f0ede8046b59
#162 adds active flag to component
Mike Becker <universe@uap-core.de>
parents:
225
diff
changeset
|
421 | active = http.param("active", ::boolValidator, true, mutableListOf()) |
184 | 422 | lead = (http.param("lead")?.toIntOrNull() ?: -1).let { |
423 | if (it < 0) null else dao.findUser(it) | |
424 | } | |
425 | // intentional defaults | |
426 | if (node.isBlank()) node = name | |
427 | // sanitizing | |
428 | node = sanitizeNode(node) | |
429 | } | |
430 | ||
431 | if (id < 0) { | |
432 | dao.insertComponent(component) | |
433 | } else { | |
434 | dao.updateComponent(component) | |
435 | } | |
436 | ||
437 | http.renderCommit("projects/${project.node}/components/") | |
438 | } | |
439 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
440 | private fun issue(http: HttpRequest, dao: DataAccessObject) { |
184 | 441 | withPathInfo(http, dao)?.run { |
442 | val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1) | |
443 | if (issue == null) { | |
444 | http.response.sendError(404) | |
445 | return | |
446 | } | |
447 | ||
448 | val comments = dao.listComments(issue) | |
449 | ||
450 | with(http) { | |
205
7725a79416f3
#115 adds custom page titles
Mike Becker <universe@uap-core.de>
parents:
200
diff
changeset
|
451 | pageTitle = "${projectInfo.project.name}: #${issue.id} ${issue.subject}" |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
452 | view = IssueDetailView(issue, comments, project, version, component) |
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
453 | feedPath = feedPath(projectInfo.project) |
184 | 454 | navigationMenu = activeProjectNavMenu( |
455 | dao.listProjects(), | |
456 | projectInfo, | |
457 | version, | |
458 | component | |
459 | ) | |
460 | styleSheets = listOf("projects") | |
207
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
461 | javascript = "issue-editor" |
184 | 462 | render("issue-view") |
463 | } | |
464 | } | |
465 | } | |
466 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
467 | private fun issueForm(http: HttpRequest, dao: DataAccessObject) { |
184 | 468 | withPathInfo(http, dao)?.run { |
469 | val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1) ?: Issue( | |
470 | -1, | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
471 | project, |
184 | 472 | ) |
473 | ||
215
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
474 | // for new issues set some defaults |
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
475 | if (issue.id < 0) { |
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
476 | // pre-select component, if available in the path info |
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
477 | issue.component = component |
184 | 478 | |
215
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
479 | // pre-select version, if available in the path info |
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
480 | if (version != null) { |
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
481 | if (version.status.isReleased) { |
231
dcb1d5a7ea3a
#163 removes multi selection for versions
Mike Becker <universe@uap-core.de>
parents:
227
diff
changeset
|
482 | issue.affected = version |
215
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
483 | } else { |
231
dcb1d5a7ea3a
#163 removes multi selection for versions
Mike Becker <universe@uap-core.de>
parents:
227
diff
changeset
|
484 | issue.resolved = version |
215
028792eda9b7
#156 fixes auto-selection overriding issue data
Mike Becker <universe@uap-core.de>
parents:
214
diff
changeset
|
485 | } |
191
193ee4828767
fixes #134 - automatic version selection
Mike Becker <universe@uap-core.de>
parents:
186
diff
changeset
|
486 | } |
193ee4828767
fixes #134 - automatic version selection
Mike Becker <universe@uap-core.de>
parents:
186
diff
changeset
|
487 | } |
193ee4828767
fixes #134 - automatic version selection
Mike Becker <universe@uap-core.de>
parents:
186
diff
changeset
|
488 | |
184 | 489 | with(http) { |
490 | view = IssueEditView( | |
491 | issue, | |
492 | projectInfo.versions, | |
493 | projectInfo.components, | |
494 | dao.listUsers(), | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
495 | project, |
184 | 496 | version, |
497 | component | |
498 | ) | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
499 | feedPath = feedPath(projectInfo.project) |
184 | 500 | navigationMenu = activeProjectNavMenu( |
501 | dao.listProjects(), | |
502 | projectInfo, | |
503 | version, | |
504 | component | |
505 | ) | |
506 | styleSheets = listOf("projects") | |
207
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
507 | javascript = "issue-editor" |
184 | 508 | render("issue-form") |
509 | } | |
510 | } | |
511 | } | |
512 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
513 | private fun issueComment(http: HttpRequest, dao: DataAccessObject) { |
184 | 514 | withPathInfo(http, dao)?.run { |
515 | val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1) | |
516 | if (issue == null) { | |
517 | http.response.sendError(404) | |
518 | return | |
519 | } | |
520 | ||
207
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
521 | val commentId = http.param("commentid")?.toIntOrNull() ?: -1 |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
522 | if (commentId > 0) { |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
523 | val comment = dao.findComment(commentId) |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
524 | if (comment == null) { |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
525 | http.response.sendError(404) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
526 | return |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
527 | } |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
528 | val originalAuthor = comment.author?.username |
207
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
529 | if (originalAuthor != null && originalAuthor == http.remoteUser) { |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
530 | val newComment = http.param("comment") |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
531 | if (!newComment.isNullOrBlank()) { |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
532 | comment.comment = newComment |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
533 | dao.updateComment(comment) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
534 | dao.insertHistoryEvent(comment) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
535 | } else { |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
536 | logger().debug("Not updating comment ${comment.id} because nothing changed.") |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
537 | } |
207
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
538 | } else { |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
539 | http.response.sendError(403) |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
540 | return |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
541 | } |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
542 | } else { |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
543 | val comment = IssueComment(-1, issue.id).apply { |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
544 | author = http.remoteUser?.let { dao.findUserByName(it) } |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
545 | comment = http.param("comment") ?: "" |
479dd7993ef9
#22 adds possibility to edit own comments
Mike Becker <universe@uap-core.de>
parents:
205
diff
changeset
|
546 | } |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
547 | val newId = dao.insertComment(comment) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
548 | dao.insertHistoryEvent(comment, newId) |
184 | 549 | } |
550 | ||
551 | http.renderCommit("${issuesHref}${issue.id}") | |
552 | } | |
553 | } | |
554 | ||
193
1e4044d29b1c
fixes missing issue sorting
Mike Becker <universe@uap-core.de>
parents:
191
diff
changeset
|
555 | private fun issueCommit(http: HttpRequest, dao: DataAccessObject) { |
184 | 556 | withPathInfo(http, dao)?.run { |
557 | val issue = Issue( | |
558 | http.param("id")?.toIntOrNull() ?: -1, | |
198
94f174d591ab
fixes wrong handling of feeds - only one channel per feed is allowed
Mike Becker <universe@uap-core.de>
parents:
193
diff
changeset
|
559 | project |
184 | 560 | ).apply { |
561 | component = dao.findComponent(http.param("component")?.toIntOrNull() ?: -1) | |
562 | category = IssueCategory.valueOf(http.param("category") ?: "") | |
563 | status = IssueStatus.valueOf(http.param("status") ?: "") | |
564 | subject = http.param("subject") ?: "" | |
565 | description = http.param("description") ?: "" | |
566 | assignee = http.param("assignee")?.toIntOrNull()?.let { | |
567 | when (it) { | |
568 | -1 -> null | |
569 | -2 -> component?.lead | |
570 | else -> dao.findUser(it) | |
571 | } | |
572 | } | |
225
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
573 | // TODO: process error messages |
87328572e36f
#159 adds release and eol dates
Mike Becker <universe@uap-core.de>
parents:
215
diff
changeset
|
574 | eta = http.param("eta", ::dateOptValidator, null, mutableListOf()) |
184 | 575 | |
231
dcb1d5a7ea3a
#163 removes multi selection for versions
Mike Becker <universe@uap-core.de>
parents:
227
diff
changeset
|
576 | affected = http.param("affected")?.toIntOrNull()?.takeIf { it > 0 }?.let { Version(it, project.id) } |
dcb1d5a7ea3a
#163 removes multi selection for versions
Mike Becker <universe@uap-core.de>
parents:
227
diff
changeset
|
577 | resolved = http.param("resolved")?.toIntOrNull()?.takeIf { it > 0 }?.let { Version(it, project.id) } |
184 | 578 | } |
579 | ||
186
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
580 | val openId = if (issue.id < 0) { |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
581 | val id = dao.insertIssue(issue) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
582 | dao.insertHistoryEvent(issue, id) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
583 | id |
186
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
584 | } else { |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
585 | val reference = dao.findIssue(issue.id) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
586 | if (reference == null) { |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
587 | http.response.sendError(404) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
588 | return |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
589 | } |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
590 | |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
591 | if (issue.hasChanged(reference)) { |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
592 | dao.updateIssue(issue) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
593 | dao.insertHistoryEvent(issue) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
594 | } else { |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
595 | logger().debug("Not updating issue ${issue.id} because nothing changed.") |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
596 | } |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
597 | |
214
69647ddb57f2
#153 adds comment box to issues form
Mike Becker <universe@uap-core.de>
parents:
210
diff
changeset
|
598 | val newComment = http.param("comment") |
69647ddb57f2
#153 adds comment box to issues form
Mike Becker <universe@uap-core.de>
parents:
210
diff
changeset
|
599 | if (!newComment.isNullOrBlank()) { |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
600 | val comment = IssueComment(-1, issue.id).apply { |
214
69647ddb57f2
#153 adds comment box to issues form
Mike Becker <universe@uap-core.de>
parents:
210
diff
changeset
|
601 | author = http.remoteUser?.let { dao.findUserByName(it) } |
69647ddb57f2
#153 adds comment box to issues form
Mike Becker <universe@uap-core.de>
parents:
210
diff
changeset
|
602 | comment = newComment |
232
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
603 | } |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
604 | val commentid = dao.insertComment(comment) |
296e12ff8d1c
#109 adds Stasi that collects intel for the feed
Mike Becker <universe@uap-core.de>
parents:
231
diff
changeset
|
605 | dao.insertHistoryEvent(comment, commentid) |
214
69647ddb57f2
#153 adds comment box to issues form
Mike Becker <universe@uap-core.de>
parents:
210
diff
changeset
|
606 | } |
186
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
607 | issue.id |
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
608 | } |
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
609 | |
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
610 | if (http.param("more") != null) { |
185
5ec9fcfbdf9c
re-enables the "create another" feature
Mike Becker <universe@uap-core.de>
parents:
184
diff
changeset
|
611 | http.renderCommit("${issuesHref}-/create") |
5ec9fcfbdf9c
re-enables the "create another" feature
Mike Becker <universe@uap-core.de>
parents:
184
diff
changeset
|
612 | } else { |
186
05eec764facd
fixes some minor migration regressions
Mike Becker <universe@uap-core.de>
parents:
185
diff
changeset
|
613 | http.renderCommit("${issuesHref}${openId}") |
185
5ec9fcfbdf9c
re-enables the "create another" feature
Mike Becker <universe@uap-core.de>
parents:
184
diff
changeset
|
614 | } |
184 | 615 | } |
616 | } | |
617 | } |