src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt

changeset 311
bf67e0ff7131
parent 307
23fe9f174d2d
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Mon Aug 05 17:41:56 2024 +0200
+++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Mon Aug 05 18:40:47 2024 +0200
@@ -27,8 +27,15 @@
 
 import de.uapcore.lightpit.*
 import de.uapcore.lightpit.dao.DataAccessObject
-import de.uapcore.lightpit.entities.*
-import de.uapcore.lightpit.types.*
+import de.uapcore.lightpit.entities.Component
+import de.uapcore.lightpit.entities.Issue
+import de.uapcore.lightpit.entities.Project
+import de.uapcore.lightpit.entities.Version
+import de.uapcore.lightpit.logic.*
+import de.uapcore.lightpit.types.VcsType
+import de.uapcore.lightpit.types.VersionStatus
+import de.uapcore.lightpit.types.WebColor
+import de.uapcore.lightpit.types.parseCommitRefs
 import de.uapcore.lightpit.viewmodel.*
 import jakarta.servlet.annotation.WebServlet
 import java.sql.Date
@@ -92,13 +99,11 @@
         }
     }
 
-    private fun feedPath(project: Project) = "feed/${project.node}/issues.rss"
-
     private fun project(http: HttpRequest, dao: DataAccessObject) {
         withPathInfo(http, dao)?.let {path ->
             val project = path.projectInfo.project
 
-            val filter = IssueFilter(http)
+            val filter = IssueFilter(http, dao)
 
             val needRelationsMap = filter.onlyBlocker
 
@@ -111,21 +116,14 @@
 
             val issues = dao.listIssues(project, filter.includeDone, specificVersion, version, specificComponent, component)
                 .sortedWith(IssueSorter(filter.sortPrimary, filter.sortSecondary, filter.sortTertiary))
-                .filter {
-                    (!filter.onlyMine || (it.assignee?.username ?: "") == (http.remoteUser ?: "<Anonymous>")) &&
-                    (!filter.onlyBlocker || (relationsMap[it.id]?.any { (_,type) -> type.blocking }?:false)) &&
-                    (filter.status.isEmpty() || filter.status.contains(it.status)) &&
-                    (filter.category.isEmpty() || filter.category.contains(it.category)) &&
-                    (filter.onlyMine || filter.assignee.isEmpty() || filter.assignee.contains(it.assignee?.id ?: -1))
-                }
+                .filter(issueFilterFunction(filter, relationsMap, http.remoteUser ?: "<Anonymous>"))
 
             with(http) {
                 pageTitle = project.name
-                view = ProjectDetails(path, issues, filter, dao.listUsers().sortedBy(User::shortDisplayname))
-                feedPath = feedPath(project)
+                view = ProjectDetails(path, issues, filter)
                 navigationMenu = projectNavMenu(dao.listProjects(), path)
                 styleSheets = listOf("projects")
-                javascript = "project-details"
+                javascript = "issue-overview"
                 render("project-details")
             }
         }
@@ -196,10 +194,9 @@
                     path.projectInfo,
                     dao.listVersionSummaries(path.projectInfo.project)
                 )
-                feedPath = feedPath(path.projectInfo.project)
                 navigationMenu = projectNavMenu(dao.listProjects(), path)
                 styleSheets = listOf("projects")
-                javascript = "project-details"
+                javascript = "issue-overview"
                 render("versions")
             }
         }
@@ -212,7 +209,6 @@
 
             with(http) {
                 view = VersionEditView(path.projectInfo, version)
-                feedPath = feedPath(path.projectInfo.project)
                 navigationMenu = projectNavMenu(dao.listProjects(), path)
                 styleSheets = listOf("projects")
                 render("version-form")
@@ -274,10 +270,9 @@
                     path.projectInfo,
                     dao.listComponentSummaries(path.projectInfo.project)
                 )
-                feedPath = feedPath(path.projectInfo.project)
                 navigationMenu = projectNavMenu(dao.listProjects(), path)
                 styleSheets = listOf("projects")
-                javascript = "project-details"
+                javascript = "issue-overview"
                 render("components")
             }
         }
@@ -290,7 +285,6 @@
 
             with(http) {
                 view = ComponentEditView(path.projectInfo, component, dao.listUsers())
-                feedPath = feedPath(path.projectInfo.project)
                 navigationMenu = projectNavMenu(dao.listProjects(), path)
                 styleSheets = listOf("projects")
                 render("component-form")
@@ -334,36 +328,8 @@
             http.response.sendError(404)
             return
         }
-        renderIssueView(http, dao, issue)
-    }
-
-    private fun renderIssueView(
-        http: HttpRequest,
-        dao: DataAccessObject,
-        issue: Issue,
-        relationError: String? = null
-    ) {
-        withPathInfo(http, dao)?.let {path ->
-            val comments = dao.listComments(issue)
-
-            with(http) {
-                pageTitle = "#${issue.id} ${issue.subject} (${path.projectInfo.project.name})"
-                view = IssueDetailView(
-                    path,
-                    issue,
-                    comments,
-                    path.projectInfo.project,
-                    dao.listIssues(path.projectInfo.project, true),
-                    dao.listIssueRelations(issue),
-                    relationError,
-                    dao.listCommitRefs(issue)
-                )
-                feedPath = feedPath(path.projectInfo.project)
-                navigationMenu = projectNavMenu(dao.listProjects(), path)
-                styleSheets = listOf("projects")
-                javascript = "issue-editor"
-                render("issue-view")
-            }
+        withPathInfo(http, dao)?.let { path ->
+            renderIssueView(http, dao, issue, path)
         }
     }
 
@@ -400,7 +366,6 @@
                     path.projectInfo.project,
                     path
                 )
-                feedPath = feedPath(path.projectInfo.project)
                 navigationMenu = projectNavMenu(dao.listProjects(), path)
                 styleSheets = listOf("projects")
                 javascript = "issue-editor"
@@ -410,44 +375,8 @@
     }
 
     private fun issueComment(http: HttpRequest, dao: DataAccessObject) {
-        withPathInfo(http, dao)?.run {
-            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
-            if (issue == null) {
-                http.response.sendError(404)
-                return
-            }
-
-            val commentId = http.param("commentid")?.toIntOrNull() ?: -1
-            if (commentId > 0) {
-                val comment = dao.findComment(commentId)
-                if (comment == null) {
-                    http.response.sendError(404)
-                    return
-                }
-                val originalAuthor = comment.author?.username
-                if (originalAuthor != null && originalAuthor == http.remoteUser) {
-                    val newComment = http.param("comment")
-                    if (!newComment.isNullOrBlank()) {
-                        comment.comment = newComment
-                        dao.updateComment(comment)
-                        dao.insertHistoryEvent(issue, comment)
-                    } else {
-                        logger.debug("Not updating comment ${comment.id} because nothing changed.")
-                    }
-                } else {
-                    http.response.sendError(403)
-                    return
-                }
-            } else {
-                val comment = IssueComment(-1, issue.id).apply {
-                    author = http.remoteUser?.let { dao.findUserByName(it) }
-                    comment = http.param("comment") ?: ""
-                }
-                val newId = dao.insertComment(comment)
-                dao.insertHistoryEvent(issue, comment, newId)
-            }
-
-            http.renderCommit("${issuesHref}${issue.id}")
+        withPathInfo(http, dao)?.let {path ->
+            commitIssueComment(http, dao, path)
         }
     }
 
@@ -456,25 +385,7 @@
             val issue = Issue(
                 http.param("id")?.toIntOrNull() ?: -1,
                 projectInfo.project
-            ).apply {
-                component = dao.findComponent(http.param("component")?.toIntOrNull() ?: -1)
-                category = IssueCategory.valueOf(http.param("category") ?: "")
-                status = IssueStatus.valueOf(http.param("status") ?: "")
-                subject = http.param("subject") ?: ""
-                description = http.param("description") ?: ""
-                assignee = http.param("assignee")?.toIntOrNull()?.let {
-                    when (it) {
-                        -1 -> null
-                        -2 -> (component?.lead ?: projectInfo.project.owner)
-                        else -> dao.findUser(it)
-                    }
-                }
-                // TODO: process error messages
-                eta = http.param("eta", ::dateOptValidator, null, mutableListOf())
-
-                affected = http.param("affected")?.toIntOrNull()?.takeIf { it > 0 }?.let { Version(it, project.id) }
-                resolved = http.param("resolved")?.toIntOrNull()?.takeIf { it > 0 }?.let { Version(it, project.id) }
-            }
+            ).applyFormData(http, dao)
 
             val openId = if (issue.id < 0) {
                 val id = dao.insertIssue(issue)
@@ -486,23 +397,7 @@
                     http.response.sendError(404)
                     return
                 }
-
-                if (issue.hasChanged(reference)) {
-                    dao.updateIssue(issue)
-                    dao.insertHistoryEvent(issue)
-                } else {
-                    logger.debug("Not updating issue ${issue.id} because nothing changed.")
-                }
-
-                val newComment = http.param("comment")
-                if (!newComment.isNullOrBlank()) {
-                    val comment = IssueComment(-1, issue.id).apply {
-                        author = http.remoteUser?.let { dao.findUserByName(it) }
-                        comment = newComment
-                    }
-                    val commentid = dao.insertComment(comment)
-                    dao.insertHistoryEvent(issue, comment, commentid)
-                }
+                processIssueForm(issue, reference, http, dao)
                 issue.id
             }
 
@@ -517,86 +412,14 @@
     }
 
     private fun issueRelation(http: HttpRequest, dao: DataAccessObject) {
-        withPathInfo(http, dao)?.run {
-            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
-            if (issue == null) {
-                http.response.sendError(404)
-                return
-            }
-            
-            // determine the relation type
-            val type: Pair<RelationType, Boolean>? = http.param("type")?.let {
-                try {
-                    if (it.startsWith("!")) {
-                        Pair(RelationType.valueOf(it.substring(1)), true)
-                    } else {
-                        Pair(RelationType.valueOf(it), false)
-                    }
-                } catch (_: IllegalArgumentException) {
-                    null
-                }
-            }
-            
-            // if the relation type was invalid, send HTTP 500
-            if (type == null) {
-                http.response.sendError(500)
-                return
-            }
-            
-            // determine the target issue
-            val targetIssue: Issue? = http.param("issue")?.let {
-                if (it.startsWith("#") && it.length > 1) {
-                    it.substring(1).split(" ", limit = 2)[0].toIntOrNull()
-                        ?.let(dao::findIssue)
-                        ?.takeIf { target -> target.project.id == issue.project.id }
-                } else {
-                    null
-                }
-            }
-
-            // check if the target issue is valid
-            if (targetIssue == null) {
-                renderIssueView(http, dao, issue, "issue.relations.target.invalid")
-                return
-            }
-            
-            // commit the result
-            dao.insertIssueRelation(IssueRelation(issue, targetIssue, type.first, type.second))
-            http.renderCommit("${issuesHref}${issue.id}")
+        withPathInfo(http, dao)?.let {path ->
+            addIssueRelation(http, dao, path)
         }
     }
 
     private fun issueRemoveRelation(http: HttpRequest, dao: DataAccessObject) {
-        withPathInfo(http, dao)?.run {
-            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
-            if (issue == null) {
-                http.response.sendError(404)
-                return
-            }
-
-            // determine relation
-            val type = http.param("type")?.let {
-                try {RelationType.valueOf(it)}
-                catch (_:IllegalArgumentException) {null}
-            }
-            if (type == null) {
-                http.response.sendError(500)
-                return
-            }
-            val rel = http.param("to")?.toIntOrNull()?.let(dao::findIssue)?.let {
-                IssueRelation(
-                    issue,
-                    it,
-                    type,
-                    http.param("reverse")?.toBoolean() ?: false
-                )
-            }
-
-            // execute removal, if there is something to remove
-            rel?.run(dao::deleteIssueRelation)
-
-            // always pretend that the operation was successful - if there was nothing to remove, it's okay
-            http.renderCommit("${issuesHref}${issue.id}")
+        withPathInfo(http, dao)?.let {path ->
+            removeIssueRelation(http, dao, path)
         }
     }
 }
\ No newline at end of file

mercurial