add author to issue history and RSS feed - fixes #463 default tip

Tue, 14 Jan 2025 20:12:25 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 14 Jan 2025 20:12:25 +0100
changeset 345
7a515768c481
parent 344
7280393d1fa7

add author to issue history and RSS feed - fixes #463

setup/postgres/psql_create_tables.sql file | annotate | diff | comparison | revisions
setup/postgres/psql_patch_1.5.0.sql file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/DataAccessObject.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/entities/IssueHistoryEntry.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/changelogs/changelog-de.jspf file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/changelogs/changelog.jspf file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issues-feed.jsp file | annotate | diff | comparison | revisions
--- a/setup/postgres/psql_create_tables.sql	Tue Jan 14 19:21:48 2025 +0100
+++ b/setup/postgres/psql_create_tables.sql	Tue Jan 14 20:12:25 2025 +0100
@@ -113,6 +113,7 @@
 (
     eventid serial primary key,
     issueid integer                  not null references lpit_issue (issueid) on delete cascade,
+    userid  integer                      null references lpit_user (userid) on delete set null,
     subject text                     not null,
     time    timestamp with time zone not null default now(),
     type    issue_history_event      not null
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/setup/postgres/psql_patch_1.5.0.sql	Tue Jan 14 20:12:25 2025 +0100
@@ -0,0 +1,4 @@
+-- apply this script to patch a version < 1.5.0 database to version 1.5.0
+
+alter table lpit_issue_history_event
+    add userid integer null references lpit_user (userid) on delete set null;
--- a/src/main/kotlin/de/uapcore/lightpit/dao/DataAccessObject.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/dao/DataAccessObject.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -118,8 +118,8 @@
     fun getIssueRelationMap(includeDone: Boolean): IssueRelationMap
     fun getIssueRelationMap(project: Project, includeDone: Boolean): IssueRelationMap
 
-    fun insertHistoryEvent(issue: Issue, newId: Int = 0)
-    fun insertHistoryEvent(issue: Issue, issueComment: IssueComment, newId: Int = 0)
+    fun insertHistoryEvent(author: User?, issue: Issue, newId: Int = 0)
+    fun insertHistoryEvent(author: User?, issue: Issue, issueComment: IssueComment, newId: Int = 0)
 
     /**
      * Lists the issue history, optionally restricted to [project], for the past [days].
--- a/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -631,15 +631,16 @@
         }
     }
 
-    override fun insertHistoryEvent(issue: Issue, newId: Int) {
+    override fun insertHistoryEvent(author: User?, issue: Issue, newId: Int) {
         val type = if (newId > 0) IssueHistoryType.New else IssueHistoryType.Update
         val issueid = if (newId > 0) newId else issue.id
 
         val eventid =
-            withStatement("insert into lpit_issue_history_event(issueid, subject, type) values (?,?,?::issue_history_event) returning eventid") {
+            withStatement("insert into lpit_issue_history_event(issueid, subject, type, userid) values (?,?,?::issue_history_event,?) returning eventid") {
                 setInt(1, issueid)
                 setString(2, issue.subject)
                 setEnum(3, type)
+                setIntOrNull(4, author?.id)
                 querySingle { it.getInt(1) }!!
             }
         withStatement(
@@ -797,15 +798,16 @@
     }
 
 
-    override fun insertHistoryEvent(issue: Issue, issueComment: IssueComment, newId: Int) {
+    override fun insertHistoryEvent(author: User?, issue: Issue, issueComment: IssueComment, newId: Int) {
         val type = if (newId > 0) IssueHistoryType.NewComment else IssueHistoryType.UpdateComment
         val commentid = if (newId > 0) newId else issueComment.id
 
         val eventid =
-            withStatement("insert into lpit_issue_history_event(issueid, subject, type) values (?,?,?::issue_history_event) returning eventid") {
+            withStatement("insert into lpit_issue_history_event(issueid, subject, type, userid) values (?,?,?::issue_history_event,?) returning eventid") {
                 setInt(1, issueComment.issueid)
                 setString(2, issue.subject)
                 setEnum(3, type)
+                setIntOrNull(4, author?.id)
                 querySingle { it.getInt(1) }!!
             }
         withStatement("insert into lpit_issue_comment_history (commentid, eventid, comment) values (?,?,?)") {
@@ -823,7 +825,7 @@
     override fun listIssueHistory(project: Project?, days: Int) =
         withStatement(
             """
-                select p.name as project_name, u.username as current_assignee, evt.*, evtdata.*
+                select evt.userid as authorid, p.name as project_name, u.username as current_assignee, evt.*, evtdata.*
                 from lpit_issue_history_event evt
                 join lpit_issue issue using (issueid)
                 join lpit_project p on project = p.projectid 
@@ -840,6 +842,9 @@
             queryAll { rs->
                 with(rs) {
                     IssueHistoryEntry(
+                        author = getInt("authorid").let {
+                            if (it > 0) findUser(it) else null
+                        },
                         project = getString("project_name"),
                         subject = getString("subject"),
                         time = getTimestamp("time"),
@@ -862,7 +867,7 @@
     override fun listIssueCommentHistory(project: Project?, days: Int) =
         withStatement(
             """
-                select u.username as current_assignee, evt.*, evtdata.*
+                select evt.userid as authorid, u.username as current_assignee, evt.*, evtdata.*
                 from lpit_issue_history_event evt
                 join lpit_issue issue using (issueid)
                 left join lpit_user u on u.userid = issue.assignee
@@ -878,6 +883,9 @@
             queryAll { rs->
                 with(rs) {
                     IssueCommentHistoryEntry(
+                        author = getInt("authorid").let {
+                            if (it > 0) findUser(it) else null
+                        },
                         subject = getString("subject"),
                         time = getTimestamp("time"),
                         type = getEnum("type"),
--- a/src/main/kotlin/de/uapcore/lightpit/entities/IssueHistoryEntry.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/entities/IssueHistoryEntry.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -32,6 +32,7 @@
 import java.sql.Timestamp
 
 class IssueHistoryEntry(
+    val author: User?,
     val project: String,
     val subject: String,
     val time: Timestamp,
@@ -52,6 +53,7 @@
 )
 
 class IssueCommentHistoryEntry(
+    val author: User?,
     val subject: String,
     val time: Timestamp,
     val type: IssueHistoryType,
--- a/src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -56,18 +56,19 @@
 }
 
 fun processIssueForm(issue: Issue, reference: Issue, http: HttpRequest, dao: DataAccessObject) {
+    val remoteUser = http.remoteUser?.let { dao.findUserByName(it) }
     if (issue.hasChanged(reference)) {
         dao.updateIssue(issue)
-        dao.insertHistoryEvent(issue)
+        dao.insertHistoryEvent(remoteUser, issue)
     }
     val newComment = http.param("comment")
     if (!newComment.isNullOrBlank()) {
         val comment = IssueComment(-1, issue.id).apply {
-            author = http.remoteUser?.let { dao.findUserByName(it) }
+            author = remoteUser
             comment = newComment
         }
         val commentid = dao.insertComment(comment)
-        dao.insertHistoryEvent(issue, comment, commentid)
+        dao.insertHistoryEvent(remoteUser, issue, comment, commentid)
     }
 }
 
@@ -83,6 +84,7 @@
 }
 
 fun processIssueComment(issue:Issue, http: HttpRequest, dao: DataAccessObject): Boolean {
+    val remoteUser = http.remoteUser?.let { dao.findUserByName(it) }
     val commentId = http.param("commentid")?.toIntOrNull() ?: -1
     if (commentId > 0) {
         val comment = dao.findComment(commentId)
@@ -90,13 +92,12 @@
             http.response.sendError(404)
             return false
         }
-        val originalAuthor = comment.author?.username
-        if (originalAuthor != null && originalAuthor == http.remoteUser) {
+        if (comment.author != null && comment.author?.id == remoteUser?.id) {
             val newComment = http.param("comment")
             if (!newComment.isNullOrBlank()) {
                 comment.comment = newComment
                 dao.updateComment(comment)
-                dao.insertHistoryEvent(issue, comment)
+                dao.insertHistoryEvent(remoteUser, issue, comment)
             }
         } else {
             http.response.sendError(403)
@@ -104,11 +105,11 @@
         }
     } else {
         val comment = IssueComment(-1, issue.id).apply {
-            author = http.remoteUser?.let { dao.findUserByName(it) }
+            author = remoteUser
             comment = http.param("comment") ?: ""
         }
         val newId = dao.insertComment(comment)
-        dao.insertHistoryEvent(issue, comment, newId)
+        dao.insertHistoryEvent(remoteUser, issue, comment, newId)
     }
     return true
 }
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -140,10 +140,11 @@
             entries.groupBy { it.issueid }.mapValues { (_, history) ->
                 history.zipWithNext().map { (cur, next) ->
                     IssueFeedEntry(
-                        cur.time, cur.type, issue = diffContent(http, cur, next)
+                        cur.author, cur.time, cur.type,
+                        issue = diffContent(http, cur, next)
                     )
                 }.plus(
-                    history.last().let { IssueFeedEntry(it.time, it.type, issue = fullContent(http, it)) }
+                    history.last().let { IssueFeedEntry(it.author, it.time, it.type, issue = fullContent(http, it)) }
                 )
             }.flatMap { it.value }
         }
@@ -155,10 +156,11 @@
             entries.groupBy { it.commentid }.mapValues { (_, history) ->
                 history.zipWithNext().map { (cur, next) ->
                     IssueFeedEntry(
-                        cur.time, cur.type, comment = diffContent(cur, next)
+                        cur.author, cur.time, cur.type,
+                        comment = diffContent(cur, next)
                     )
                 }.plus(
-                    history.last().let { IssueFeedEntry(it.time, it.type, comment = fullContent(it)) }
+                    history.last().let { IssueFeedEntry(it.author, it.time, it.type, comment = fullContent(it)) }
                 )
             }.flatMap { it.value }
         }
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -395,8 +395,9 @@
             ).applyFormData(http, dao)
 
             val openId = if (issue.id < 0) {
+                val remoteUser = http.remoteUser?.let { dao.findUserByName(it) }
                 val id = dao.insertIssue(issue)
-                dao.insertHistoryEvent(issue, id)
+                dao.insertHistoryEvent(remoteUser, issue, id)
                 id
             } else {
                 val reference = dao.findIssue(issue.id)
--- a/src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt	Tue Jan 14 20:12:25 2025 +0100
@@ -26,6 +26,7 @@
 package de.uapcore.lightpit.viewmodel
 
 import de.uapcore.lightpit.entities.Project
+import de.uapcore.lightpit.entities.User
 import de.uapcore.lightpit.types.IssueHistoryType
 import java.sql.Timestamp
 import java.time.Instant
@@ -55,6 +56,7 @@
 )
 
 class IssueFeedEntry(
+    val author: User?,
     val time: Timestamp,
     val type: IssueHistoryType,
     val issue: IssueDiff? = null,
--- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf	Tue Jan 14 20:12:25 2025 +0100
@@ -27,11 +27,13 @@
 <h3>Version 1.5.0 (Vorschau)</h3>
 
 <ul>
+    <li>Autor im RSS-Feed hinzugefügt.</li>
     <li>Projekt und Komponente sind nun in der Vorgangsansicht direkt verlinkt.</li>
     <li>
         Query Parameter <code>in_project</code> zu globalen Vorgangs-URLs hinzugefügt,
         der von Tools benutzt werden kann, um Vorgänge direkt in der Projektansicht zu öffnen.
     </li>
+    <li>Fehler behoben, bei dem der "Assignee"-Filter im RSS-Feed nicht auf Kommentare angewendet wurde.</li>
     <li>Versionsinformationen werden nun korrekt in die Vorgangshistorie geschrieben (relevant für RSS-Feeds).</li>
     <li>
         Fehler behoben, bei dem die Formulare für Versionen und Vorgänge existierende Datumsfelder mit dem
--- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf	Tue Jan 14 20:12:25 2025 +0100
@@ -27,11 +27,13 @@
 <h3>Version 1.5.0 (preview)</h3>
 
 <ul>
+    <li>Add author to RSS feed.</li>
     <li>Add links to project and component in the tabular issue view.</li>
     <li>
         Add optional query parameter <code>in_project</code> for global issue URLs
         that can be used by tools to directly open an issue in the project view.
     </li>
+    <li>Fix that assignee filter does not work for comments in RSS feed.</li>
     <li>
         Fix missing affected and target versions in issue history
         (which is why they were never shown in the RSS feed).
--- a/src/main/webapp/WEB-INF/jsp/issues-feed.jsp	Tue Jan 14 19:21:48 2025 +0100
+++ b/src/main/webapp/WEB-INF/jsp/issues-feed.jsp	Tue Jan 14 20:12:25 2025 +0100
@@ -80,6 +80,9 @@
             <link>${link}</link>
             <guid isPermaLink="true">${link}</guid>
             <pubDate><fmt:formatDate value="${entry.time}" pattern="EEE, dd MMM yyyy HH:mm:ss zzz"/></pubDate>
+            <c:if test="${not empty entry.author}">
+            <author><c:out value="${entry.author.displayname}"/> </author>
+            </c:if>
         </item>
     </c:forEach>
 </channel>

mercurial