Mon, 02 Aug 2021 17:04:17 +0200
#22 adds possibility to edit own comments
1.1 --- a/src/main/kotlin/de/uapcore/lightpit/Constants.kt Mon Aug 02 15:13:04 2021 +0200 1.2 +++ b/src/main/kotlin/de/uapcore/lightpit/Constants.kt Mon Aug 02 17:04:17 2021 +0200 1.3 @@ -92,6 +92,11 @@ 1.4 const val REQ_ATTR_STYLESHEET = "extraCss" 1.5 1.6 /** 1.7 + * Key for the name of the optional java script file. 1.8 + */ 1.9 + const val REQ_ATTR_JAVASCRIPT = "javascriptFile" 1.10 + 1.11 + /** 1.12 * Key for a location the page shall redirect to. 1.13 * Will be used in a meta element. 1.14 */
2.1 --- a/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Mon Aug 02 15:13:04 2021 +0200 2.2 +++ b/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Mon Aug 02 17:04:17 2021 +0200 2.3 @@ -60,7 +60,7 @@ 2.4 /** 2.5 * The name of the content page. 2.6 * 2.7 - * @see Constants#REQ_ATTR_CONTENT_PAGE 2.8 + * @see Constants#REQ_ATTR_PAGE_TITLE 2.9 */ 2.10 var pageTitle = "" 2.11 set(value) { 2.12 @@ -82,6 +82,19 @@ 2.13 } 2.14 2.15 /** 2.16 + * A list of additional style sheets. 2.17 + * 2.18 + * @see Constants#REQ_ATTR_JAVASCRIPT 2.19 + */ 2.20 + var javascript = "" 2.21 + set(value) { 2.22 + field = value 2.23 + request.setAttribute(Constants.REQ_ATTR_JAVASCRIPT, 2.24 + value.withExt(".js") 2.25 + ) 2.26 + } 2.27 + 2.28 + /** 2.29 * The name of the navigation menu JSP. 2.30 * 2.31 * @see Constants#REQ_ATTR_NAVIGATION
3.1 --- a/src/main/kotlin/de/uapcore/lightpit/dao/DataAccessObject.kt Mon Aug 02 15:13:04 2021 +0200 3.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/DataAccessObject.kt Mon Aug 02 17:04:17 2021 +0200 3.3 @@ -75,5 +75,7 @@ 3.4 fun updateIssue(issue: Issue) 3.5 3.6 fun listComments(issue: Issue): List<IssueComment> 3.7 + fun findComment(id: Int): IssueComment? 3.8 fun insertComment(issueComment: IssueComment) 3.9 + fun updateComment(issueComment: IssueComment) 3.10 } 3.11 \ No newline at end of file
4.1 --- a/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Mon Aug 02 15:13:04 2021 +0200 4.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Mon Aug 02 17:04:17 2021 +0200 4.3 @@ -658,6 +658,12 @@ 4.4 queryAll { it.extractIssueComment() } 4.5 } 4.6 4.7 + override fun findComment(id: Int): IssueComment? = 4.8 + withStatement("select * from lpit_issue_comment left join lpit_user using (userid) where commentid = ?") { 4.9 + setInt(1, id) 4.10 + querySingle { it.extractIssueComment() } 4.11 + } 4.12 + 4.13 override fun insertComment(issueComment: IssueComment) { 4.14 useStatement("update lpit_issue set updated = now() where issueid = ?") { updateIssueDate -> 4.15 withStatement("insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)") { 4.16 @@ -672,5 +678,19 @@ 4.17 } 4.18 } 4.19 } 4.20 + 4.21 + override fun updateComment(issueComment: IssueComment) { 4.22 + useStatement("update lpit_issue set updated = now() where issueid = ?") { updateIssueDate -> 4.23 + withStatement("update lpit_issue_comment set comment = ?, updatecount = updatecount + 1, updated = now() where commentid = ?") { 4.24 + with(issueComment) { 4.25 + updateIssueDate.setInt(1, issueid) 4.26 + setStringSafe(1, comment) 4.27 + setInt(2, id) 4.28 + } 4.29 + executeUpdate() 4.30 + updateIssueDate.executeUpdate() 4.31 + } 4.32 + } 4.33 + } 4.34 //</editor-fold> 4.35 } 4.36 \ No newline at end of file
5.1 --- a/src/main/kotlin/de/uapcore/lightpit/entities/IssueComment.kt Mon Aug 02 15:13:04 2021 +0200 5.2 +++ b/src/main/kotlin/de/uapcore/lightpit/entities/IssueComment.kt Mon Aug 02 17:04:17 2021 +0200 5.3 @@ -30,6 +30,7 @@ 5.4 5.5 data class IssueComment(override val id: Int, val issueid: Int) : Entity { 5.6 var author: User? = null 5.7 + var commentFormatted: String = "" 5.8 var comment: String = "" 5.9 var created: Timestamp = Timestamp.from(Instant.now()) 5.10 var updated: Timestamp = Timestamp.from(Instant.now())
6.1 --- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Mon Aug 02 15:13:04 2021 +0200 6.2 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Mon Aug 02 17:04:17 2021 +0200 6.3 @@ -441,7 +441,6 @@ 6.4 with(http) { 6.5 pageTitle = "${projectInfo.project.name}: #${issue.id} ${issue.subject}" 6.6 view = IssueDetailView(issue, comments, project, version, component) 6.7 - // TODO: feed path for this particular issue 6.8 feedPath = feedPath(projectInfo.project) 6.9 navigationMenu = activeProjectNavMenu( 6.10 dao.listProjects(), 6.11 @@ -450,6 +449,7 @@ 6.12 component 6.13 ) 6.14 styleSheets = listOf("projects") 6.15 + javascript = "issue-editor" 6.16 render("issue-view") 6.17 } 6.18 } 6.19 @@ -492,6 +492,7 @@ 6.20 component 6.21 ) 6.22 styleSheets = listOf("projects") 6.23 + javascript = "issue-editor" 6.24 render("issue-form") 6.25 } 6.26 } 6.27 @@ -506,13 +507,26 @@ 6.28 } 6.29 6.30 // TODO: throw validator exception instead of using a default 6.31 - val comment = IssueComment(-1, issue.id).apply { 6.32 - author = http.remoteUser?.let { dao.findUserByName(it) } 6.33 - comment = http.param("comment") ?: "" 6.34 + 6.35 + val commentId = http.param("commentid")?.toIntOrNull() ?: -1 6.36 + if (commentId > 0) { 6.37 + val comment = dao.findComment(commentId) 6.38 + val originalAuthor = comment?.author?.username 6.39 + if (originalAuthor != null && originalAuthor == http.remoteUser) { 6.40 + comment.comment = http.param("comment") ?: "" 6.41 + dao.updateComment(comment) 6.42 + } else { 6.43 + http.response.sendError(403) 6.44 + return 6.45 + } 6.46 + } else { 6.47 + val comment = IssueComment(-1, issue.id).apply { 6.48 + author = http.remoteUser?.let { dao.findUserByName(it) } 6.49 + comment = http.param("comment") ?: "" 6.50 + } 6.51 + dao.insertComment(comment) 6.52 } 6.53 6.54 - dao.insertComment(comment) 6.55 - 6.56 http.renderCommit("${issuesHref}${issue.id}") 6.57 } 6.58 }
7.1 --- a/src/main/kotlin/de/uapcore/lightpit/viewmodel/Issues.kt Mon Aug 02 15:13:04 2021 +0200 7.2 +++ b/src/main/kotlin/de/uapcore/lightpit/viewmodel/Issues.kt Mon Aug 02 17:04:17 2021 +0200 7.3 @@ -78,7 +78,7 @@ 7.4 7.5 issue.description = process(issue.description?:"") 7.6 for (comment in comments) { 7.7 - comment.comment = process(comment.comment) 7.8 + comment.commentFormatted = process(comment.comment) 7.9 } 7.10 } 7.11 }
8.1 --- a/src/main/resources/localization/strings.properties Mon Aug 02 15:13:04 2021 +0200 8.2 +++ b/src/main/resources/localization/strings.properties Mon Aug 02 17:04:17 2021 +0200 8.3 @@ -25,6 +25,7 @@ 8.4 app.license.title=License 8.5 app.name=Lightweight Project and Issue Tracking 8.6 button.cancel=Cancel 8.7 +button.comment.edit=Edit Comment 8.8 button.comment=Comment 8.9 button.component.create=New Component 8.10 button.component.edit=Edit Component 8.11 @@ -70,6 +71,8 @@ 8.12 issue.category.Test=Test 8.13 issue.category=Category 8.14 issue.comments.anonauthor=Anonymous Author 8.15 +issue.comments.lastupdate=Last edited: 8.16 +issue.comments.updateCount=total edits 8.17 issue.comments=Comments 8.18 issue.created=Created 8.19 issue.description=Description
9.1 --- a/src/main/resources/localization/strings_de.properties Mon Aug 02 15:13:04 2021 +0200 9.2 +++ b/src/main/resources/localization/strings_de.properties Mon Aug 02 17:04:17 2021 +0200 9.3 @@ -24,6 +24,7 @@ 9.4 app.changelog=Versionshistorie 9.5 app.license.title=Lizenz (Englisch) 9.6 button.cancel=Abbrechen 9.7 +button.comment.edit=Absenden 9.8 button.comment=Kommentieren 9.9 button.component.create=Neue Komponente 9.10 button.component.edit=Komponente Bearbeiten 9.11 @@ -55,11 +56,11 @@ 9.12 error.message = Server Nachricht 9.13 error.returnLink = Kehre zurück zu 9.14 error.timestamp = Zeitstempel 9.15 -feed=Feed 9.16 feed.issues.created=Vorgang wurde erstellt. 9.17 feed.issues.description=Feed \u00fcber k\u00fcrzlich aktualisierte Vorg\u00e4nge. 9.18 feed.issues.title=LightPIT Vorg\u00e4nge 9.19 feed.issues.updated=Vorgang wurde aktualisiert. 9.20 +feed=Feed 9.21 issue.affected-versions=Betroffene Versionen 9.22 issue.assignee=Zugewiesen 9.23 issue.category.Bug=Fehler 9.24 @@ -69,6 +70,8 @@ 9.25 issue.category.Test=Test 9.26 issue.category=Kategorie 9.27 issue.comments.anonauthor=Anonymer Autor 9.28 +issue.comments.lastupdate=Zuletzt bearbeitet: 9.29 +issue.comments.updateCount=mal bearbeitet 9.30 issue.comments=Kommentare 9.31 issue.created=Erstellt 9.32 issue.description=Beschreibung
10.1 --- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Mon Aug 02 15:13:04 2021 +0200 10.2 +++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Mon Aug 02 17:04:17 2021 +0200 10.3 @@ -28,6 +28,7 @@ 10.4 10.5 <ul> 10.6 <li>Infoseite hinzugefügt.</li> 10.7 + <li>Eigene Kommentare können nun bearbeitet werden.</li> 10.8 <li>Sortierreihenfolge der Versionen in der Übersicht an die Sortierreihenfolge im Seitenmenü angeglichen.</li> 10.9 <li>Duplikate in Komponenten- und Versionslisten behoben.</li> 10.10 <li>Fehler behoben, bei dem vorbereitete Datenbankabfragen nicht geschlossen wurden.</li>
11.1 --- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf Mon Aug 02 15:13:04 2021 +0200 11.2 +++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf Mon Aug 02 17:04:17 2021 +0200 11.3 @@ -28,6 +28,7 @@ 11.4 11.5 <ul> 11.6 <li>Adds about page.</li> 11.7 + <li>Adds possibility to edit own comments.</li> 11.8 <li>Changes sort order of versions in the versions overview to be the same as in the left menu.</li> 11.9 <li>Fixes duplicates in the components and versions lists.</li> 11.10 <li>Fixes leaking prepared statements.</li>
12.1 --- a/src/main/webapp/WEB-INF/jsp/issue-view.jsp Mon Aug 02 15:13:04 2021 +0200 12.2 +++ b/src/main/webapp/WEB-INF/jsp/issue-view.jsp Mon Aug 02 17:04:17 2021 +0200 12.3 @@ -175,15 +175,18 @@ 12.4 </form> 12.5 <c:forEach var="comment" items="${viewmodel.comments}"> 12.6 <div class="comment"> 12.7 - <div class="caption"> 12.8 + <div class="comment-author"> 12.9 <c:if test="${not empty comment.author}"> 12.10 <c:if test="${not empty comment.author.mail}"> 12.11 - <a href="mailto:${comment.author.mail}"> 12.12 + <a class="comment-author-name" href="mailto:${comment.author.mail}"> 12.13 </c:if> 12.14 <c:out value="${comment.author.displayname}"/> 12.15 <c:if test="${not empty comment.author.mail}"> 12.16 </a> 12.17 </c:if> 12.18 + <c:if test="${comment.author.username eq pageContext.request.remoteUser}"> 12.19 + <a class="comment-edit-icon" onclick="showCommentEditor(${comment.id})">✎</a> 12.20 + </c:if> 12.21 </c:if> 12.22 <c:if test="${empty comment.author}"> 12.23 <fmt:message key="issue.comments.anonauthor"/> 12.24 @@ -192,11 +195,34 @@ 12.25 <div class="smalltext"> 12.26 <fmt:formatDate type="BOTH" value="${comment.created}" /> 12.27 <c:if test="${comment.updateCount gt 0}"> 12.28 - <!-- TODO: update count --> 12.29 + <span class="comment-edit-info"> 12.30 + (<fmt:message key="issue.comments.lastupdate"/> <fmt:formatDate type="BOTH" value="${comment.updated}" />, ${comment.updateCount} <fmt:message key="issue.comments.updateCount"/>) 12.31 + </span> 12.32 </c:if> 12.33 </div> 12.34 - <div class="medskip markdown-styled"> 12.35 - ${comment.comment} 12.36 + <div id="comment-view-${comment.id}" class="medskip markdown-styled"> 12.37 + ${comment.commentFormatted} 12.38 + </div> 12.39 + <div id="comment-editor-${comment.id}" style="display: none"> 12.40 + <form id="comment-form-${comment.id}" action="${issuesHref}${issue.id}/comment" method="post"> 12.41 + <input type="hidden" name="commentid" value="${comment.id}"> 12.42 + <table class="formtable fullwidth"> 12.43 + <tbody> 12.44 + <tr> 12.45 + <td> 12.46 + <textarea rows="5" name="comment" required><c:out value="${comment.comment}"/></textarea> 12.47 + </td> 12.48 + </tr> 12.49 + </tbody> 12.50 + <tfoot> 12.51 + <tr> 12.52 + <td> 12.53 + <button type="submit"><fmt:message key="button.comment.edit"/></button> 12.54 + </td> 12.55 + </tr> 12.56 + </tfoot> 12.57 + </table> 12.58 + </form> 12.59 </div> 12.60 </div> 12.61 </c:forEach>
13.1 --- a/src/main/webapp/WEB-INF/jsp/site.jsp Mon Aug 02 15:13:04 2021 +0200 13.2 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Mon Aug 02 17:04:17 2021 +0200 13.3 @@ -30,6 +30,9 @@ 13.4 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 13.5 <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 13.6 13.7 +<%-- Version suffix for forcing browsers to update the CSS / JS files --%> 13.8 +<c:set scope="page" var="versionSuffix" value="20210802"/> 13.9 + 13.10 <%-- Make the base href easily available at request scope --%> 13.11 <c:set scope="page" var="baseHref" value="${requestScope[Constants.REQ_ATTR_BASE_HREF]}"/> 13.12 13.13 @@ -54,6 +57,9 @@ 13.14 <%-- Define an alias for the additional stylesheet --%> 13.15 <c:set scope="page" var="extraCss" value="${requestScope[Constants.REQ_ATTR_STYLESHEET]}"/> 13.16 13.17 +<%-- Define an alias for the optional JS file --%> 13.18 +<c:set scope="page" var="javascriptFile" value="${requestScope[Constants.REQ_ATTR_JAVASCRIPT]}"/> 13.19 + 13.20 <%-- Load resource bundle --%> 13.21 <fmt:setLocale scope="request" value="${pageContext.response.locale}"/> 13.22 <fmt:setBundle scope="request" basename="localization.strings"/> 13.23 @@ -70,15 +76,18 @@ 13.24 <c:if test="${not empty redirectLocation}"> 13.25 <meta http-equiv="refresh" content="0; URL=${redirectLocation}"> 13.26 </c:if> 13.27 - <link rel="stylesheet" href="lightpit.css" type="text/css"> 13.28 + <link rel="stylesheet" href="lightpit.css?v=${versionSuffix}" type="text/css"> 13.29 <c:if test="${not empty feedHref}"> 13.30 <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="${feedHref}"/> 13.31 </c:if> 13.32 <c:if test="${not empty extraCss}"> 13.33 <c:forEach items="${extraCss}" var="cssFile"> 13.34 - <link rel="stylesheet" href="${cssFile}" type="text/css"> 13.35 + <link rel="stylesheet" href="${cssFile}?v=${versionSuffix}" type="text/css"> 13.36 </c:forEach> 13.37 </c:if> 13.38 + <c:if test="${not empty javascriptFile}"> 13.39 + <script src="${javascriptFile}?v=${versionSuffix}" type="text/javascript"></script> 13.40 + </c:if> 13.41 </head> 13.42 <body> 13.43 <div id="mainMenu">
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/src/main/webapp/issue-editor.js Mon Aug 02 17:04:17 2021 +0200 14.3 @@ -0,0 +1,36 @@ 14.4 +/* 14.5 + * Copyright 2021 Mike Becker. All rights reserved. 14.6 + * 14.7 + * Redistribution and use in source and binary forms, with or without 14.8 + * modification, are permitted provided that the following conditions are met: 14.9 + * 14.10 + * 1. Redistributions of source code must retain the above copyright 14.11 + * notice, this list of conditions and the following disclaimer. 14.12 + * 14.13 + * 2. Redistributions in binary form must reproduce the above copyright 14.14 + * notice, this list of conditions and the following disclaimer in the 14.15 + * documentation and/or other materials provided with the distribution. 14.16 + * 14.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 14.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 14.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 14.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 14.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 14.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 14.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 14.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14.27 + */ 14.28 + 14.29 +/** 14.30 + * Replaces the formatted comment text with an text area. 14.31 + * 14.32 + * @param {number} id the ID of the comment 14.33 + */ 14.34 +function showCommentEditor(id) { 14.35 + const editor = document.getElementById(`comment-editor-${id}`) 14.36 + const view = document.getElementById(`comment-view-${id}`) 14.37 + editor.style.display='block' 14.38 + view.style.display='none' 14.39 +}
15.1 --- a/src/main/webapp/lightpit.css Mon Aug 02 15:13:04 2021 +0200 15.2 +++ b/src/main/webapp/lightpit.css Mon Aug 02 17:04:17 2021 +0200 15.3 @@ -43,6 +43,7 @@ 15.4 } 15.5 15.6 a { 15.7 + cursor: pointer; 15.8 color: #3060f8; 15.9 text-decoration: none; 15.10 }
16.1 --- a/src/main/webapp/projects.css Mon Aug 02 15:13:04 2021 +0200 16.2 +++ b/src/main/webapp/projects.css Mon Aug 02 17:04:17 2021 +0200 16.3 @@ -147,9 +147,30 @@ 16.4 } 16.5 16.6 div.comment { 16.7 + padding-left: .25rem; 16.8 margin-bottom: 1.25em; 16.9 } 16.10 16.11 +.comment-author { 16.12 + color: #3060f8; 16.13 + background: #e7e7ef; 16.14 + margin-left: -.25rem; 16.15 + padding: .25rem; 16.16 +} 16.17 + 16.18 +.comment-author-name { 16.19 + color: inherit; 16.20 +} 16.21 + 16.22 +.comment-edit-icon { 16.23 + margin-left: 1ex; 16.24 +} 16.25 + 16.26 +span.comment-edit-info { 16.27 + margin-left: 1ex; 16.28 + color: #556080; 16.29 +} 16.30 + 16.31 span.eta-overdue { 16.32 color: red; 16.33 } 16.34 \ No newline at end of file