adds issue detail view - fixes #24

Fri, 23 Oct 2020 13:29:33 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 23 Oct 2020 13:29:33 +0200
changeset 146
b0e83cab0bde
parent 145
6d2d69fd1c12
child 147
dfec8c5f8db0

adds issue detail view - fixes #24

src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/viewmodel/IssueDetailView.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java file | annotate | diff | comparison | revisions
src/main/resources/localization/projects.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/projects_de.properties file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issue-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issue-view.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jspf/issue-list.jspf file | annotate | diff | comparison | revisions
src/main/webapp/lightpit.css file | annotate | diff | comparison | revisions
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Fri Oct 23 13:29:33 2020 +0200
@@ -422,16 +422,39 @@
 
     private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
         final var project = viewModel.getProjectInfo().getProject();
-        issue.setProject(project);
+        issue.setProject(project); // automatically set current project for new issues
         viewModel.setIssue(issue);
         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
         viewModel.setUsers(dao.getUserDao().list());
         viewModel.setComponents(dao.getComponentDao().list(project));
-        if (issue.getId() >= 0) {
-            viewModel.setComments(dao.getIssueDao().listComments(issue));
-        }
     }
 
+    @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
+    public ResponseType viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
+        final var viewModel = new IssueDetailView();
+        populate(viewModel, pathParameters, dao);
+
+        final var projectInfo = viewModel.getProjectInfo();
+        if (projectInfo == null) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return ResponseType.NONE;
+        }
+
+        final var issueDao = dao.getIssueDao();
+        final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
+        if (issue == null) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return ResponseType.NONE;
+        }
+
+        issueDao.joinVersionInformation(issue);
+        viewModel.setIssue(issue);
+        viewModel.setComments(issueDao.listComments(issue));
+
+        return forwardView(req, viewModel, "issue-view");
+    }
+
+    // TODO: why should the issue editor be child of $project?
     @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
         final var viewModel = new IssueEditView();
@@ -522,8 +545,8 @@
 
             dao.getIssueDao().saveOrUpdate(issue, issue.getProject());
 
-            // TODO: fix issue #14
-            setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/all-components/all-versions/issues/");
+            // TODO: fix redirect location
+            setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
 
             return ResponseType.HTML;
@@ -561,8 +584,8 @@
 
             dao.getIssueDao().saveComment(issueComment);
 
-            // TODO: fix redirect location (e.g. after fixing #24)
-            setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/edit");
+            // TODO: fix redirect location
+            setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
 
             return ResponseType.HTML;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssueDetailView.java	Fri Oct 23 13:29:33 2020 +0200
@@ -0,0 +1,28 @@
+package de.uapcore.lightpit.viewmodel;
+
+import de.uapcore.lightpit.entities.Issue;
+import de.uapcore.lightpit.entities.IssueComment;
+
+import java.util.List;
+
+public class IssueDetailView extends ProjectView {
+    private Issue issue;
+
+    private List<IssueComment> comments;
+
+    public void setIssue(Issue issue) {
+        this.issue = issue;
+    }
+
+    public Issue getIssue() {
+        return issue;
+    }
+
+    public List<IssueComment> getComments() {
+        return comments;
+    }
+
+    public void setComments(List<IssueComment> comments) {
+        this.comments = comments;
+    }
+}
--- a/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java	Fri Oct 23 13:29:33 2020 +0200
@@ -4,23 +4,12 @@
 
 import java.util.*;
 
-public class IssueEditView extends ProjectView {
-    private Issue issue;
-
+public class IssueEditView extends IssueDetailView {
     private List<Project> projects = Collections.emptyList();
     private Set<Version> versionsUpcoming = new HashSet<>();
     private Set<Version> versionsRecent = new HashSet<>();
     private List<User> users;
     private List<Component> components;
-    private List<IssueComment> comments;
-
-    public void setIssue(Issue issue) {
-        this.issue = issue;
-    }
-
-    public Issue getIssue() {
-        return issue;
-    }
 
     public List<Project> getProjects() {
         return projects;
@@ -42,8 +31,8 @@
         versionsRecent.clear();
         versionsUpcoming.clear();
         // keep the current selection, if any
-        versionsRecent.addAll(issue.getAffectedVersions());
-        versionsUpcoming.addAll(issue.getResolvedVersions());
+        versionsRecent.addAll(getIssue().getAffectedVersions());
+        versionsUpcoming.addAll(getIssue().getResolvedVersions());
         for (var v : versions) {
             if (v.getStatus().isReleased()) {
                 if (!v.getStatus().equals(VersionStatus.Deprecated))
@@ -77,12 +66,4 @@
     public IssueCategory[] getIssueCategory() {
         return IssueCategory.values();
     }
-
-    public List<IssueComment> getComments() {
-        return comments;
-    }
-
-    public void setComments(List<IssueComment> comments) {
-        this.comments = comments;
-    }
 }
--- a/src/main/resources/localization/projects.properties	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/resources/localization/projects.properties	Fri Oct 23 13:29:33 2020 +0200
@@ -29,6 +29,7 @@
 button.version.create=New Version
 button.version.edit=Edit Version
 button.issue.create=New Issue
+button.issue.edit=Edit Issue
 button.issue.all=All Issues
 button.comment=Comment
 
--- a/src/main/resources/localization/projects_de.properties	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/resources/localization/projects_de.properties	Fri Oct 23 13:29:33 2020 +0200
@@ -29,6 +29,7 @@
 button.version.create=Neue Version
 button.version.edit=Version Bearbeiten
 button.issue.create=Neuer Vorgang
+button.issue.edit=Vorgang Bearbeiten
 button.issue.all=Alle Vorg\u00e4nge
 button.comment=Kommentieren
 
--- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp	Fri Oct 23 13:29:33 2020 +0200
@@ -169,56 +169,20 @@
         <tr>
             <td colspan="2">
                 <input type="hidden" name="id" value="${issue.id}"/>
-                <%-- TODO: fix #14 --%>
-                <a href="./projects/${issue.project.node}/all-components/all-versions/issues/" class="button">
+                <c:if test="${issue.id ge 0}">
+                <a href="./projects/${issue.project.node}/issues/${issue.id}/view" class="button">
                     <fmt:message bundle="${lightpit_bundle}" key="button.cancel"/>
                 </a>
+                </c:if>
+                <c:if test="${issue.id lt 0}">
+                    <%-- TODO: fix #14 --%>
+                    <a href="./projects/${issue.project.node}/all-components/all-versions/issues/" class="button">
+                        <fmt:message bundle="${lightpit_bundle}" key="button.cancel"/>
+                    </a>
+                </c:if>
                 <button type="submit"><fmt:message bundle="${lightpit_bundle}" key="button.okay"/></button>
             </td>
         </tr>
         </tfoot>
     </table>
 </form>
-<hr class="comments-separator"/>
-<h2><fmt:message key="issue.comments"/></h2>
-<c:if test="${viewmodel.issue.id ge 0}">
-<form id="comment-form" action="./projects/commit-issue-comment" method="post">
-    <table class="formtable fullwidth">
-        <tbody>
-            <tr>
-                <td><textarea rows="5" name="comment" required></textarea></td>
-            </tr>
-        </tbody>
-        <tfoot>
-            <tr>
-                <td>
-                    <input type="hidden" name="issueid" value="${issue.id}"/>
-                    <button type="submit"><fmt:message key="button.comment"/></button>
-                </td>
-            </tr>
-        </tfoot>
-    </table>
-</form>
-    <c:forEach var="comment" items="${viewmodel.comments}">
-        <div class="comment">
-            <div class="caption">
-                <c:if test="${not empty comment.author}">
-                    <c:out value="${comment.author.displayname}"/>
-                </c:if>
-                <c:if test="${empty comment.author}">
-                    <fmt:message key="issue.comments.anonauthor"/>
-                </c:if>
-            </div>
-            <div class="smalltext">
-                <fmt:formatDate type="BOTH" value="${comment.created}" />
-                <c:if test="${comment.updateCount gt 0}">
-                    <!-- TODO: update count -->
-                </c:if>
-            </div>
-            <div class="medskip">
-                <c:out value="${comment.comment}"/>
-            </div>
-        </div>
-    </c:forEach>
-</c:if>
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/WEB-INF/jsp/issue-view.jsp	Fri Oct 23 13:29:33 2020 +0200
@@ -0,0 +1,186 @@
+<%--
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2018 Mike Becker. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--%>
+<%@page pageEncoding="UTF-8" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+
+<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.IssueDetailView" scope="request"/>
+<c:set var="issue" scope="page" value="${viewmodel.issue}" />
+
+<table class="formtable fullwidth">
+    <colgroup>
+        <col>
+        <col style="width: 100%">
+    </colgroup>
+    <tbody>
+    <c:if test="${viewmodel.issue.id ge 0}">
+    <tr>
+        <th><fmt:message key="issue.id"/></th>
+        <td>${issue.id}</td>
+    </tr>
+    </c:if>
+    <tr>
+        <th><fmt:message key="issue.project"/></th>
+        <td>
+            <c:out value="${issue.project.name}" />
+        </td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.created"/></th>
+        <td><fmt:formatDate value="${issue.created}" /></td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.updated"/></th>
+        <td><fmt:formatDate value="${issue.updated}" /></td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.component"/></th>
+        <td>
+            <c:if test="${not empty issue.component}">
+                <c:out value="${issue.component.name}"/>
+            </c:if>
+            <c:if test="${empty issue.component}">
+                <fmt:message key="placeholder.null-component"/>
+            </c:if>
+        </td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.category"/></th>
+        <td>
+            <div class="issue-tag ${issue.category}" style="width: auto">
+                <fmt:message key="issue.category.${issue.category}" />
+            </div>
+        </td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.status"/></th>
+        <td>
+            <div class="issue-tag phase-${issue.status.phase}" style="width: auto">
+                <fmt:message key="issue.status.${issue.status}" />
+            </div>
+        </td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.subject"/></th>
+        <td><c:out value="${issue.subject}"/></td>
+    </tr>
+    <tr>
+        <th class="vtop"><fmt:message key="issue.description"/></th>
+        <td>
+            <textarea readonly rows="10"><c:out value="${issue.description}"/></textarea>
+        </td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.assignee"/></th>
+        <td>
+            <c:if test="${not empty issue.assignee}">
+                <c:out value="${issue.assignee.displayname}"/>
+            </c:if>
+            <c:if test="${empty issue.assignee}">
+                <fmt:message key="placeholder.null-assignee" />
+            </c:if>
+        </td>
+    </tr>
+    <tr>
+        <th class="vtop"><fmt:message key="issue.affected-versions"/></th>
+        <td>
+            <c:forEach var="version" items="${issue.affectedVersions}">
+                <c:out value="${version.name}"/>
+            </c:forEach>
+        </td>
+    </tr>
+    <tr>
+        <th class="vtop"><fmt:message key="issue.resolved-versions"/></th>
+        <td>
+            <c:forEach var="version" items="${issue.resolvedVersions}">
+                <c:out value="${version.name}"/>
+            </c:forEach>
+        </td>
+    </tr>
+    <tr>
+        <th><fmt:message key="issue.eta"/></th>
+        <td><fmt:formatDate value="${issue.eta}" /></td>
+    </tr>
+    </tbody>
+    <tfoot>
+    <tr>
+        <td colspan="2">
+            <%-- TODO: fix #14 --%>
+            <a href="./projects/${issue.project.node}/all-components/all-versions/issues/" class="button">
+                <fmt:message bundle="${lightpit_bundle}" key="button.cancel"/>
+            </a>
+            <a href="./projects/${issue.project.node}/issues/${issue.id}/edit" class="button submit">
+                <fmt:message key="button.issue.edit"/>
+            </a>
+        </td>
+    </tr>
+    </tfoot>
+</table>
+
+<hr class="comments-separator"/>
+<h2><fmt:message key="issue.comments"/></h2>
+<c:if test="${viewmodel.issue.id ge 0}">
+<form id="comment-form" action="./projects/commit-issue-comment" method="post">
+    <table class="formtable fullwidth">
+        <tbody>
+            <tr>
+                <td><textarea rows="5" name="comment" required></textarea></td>
+            </tr>
+        </tbody>
+        <tfoot>
+            <tr>
+                <td>
+                    <input type="hidden" name="issueid" value="${issue.id}"/>
+                    <button type="submit"><fmt:message key="button.comment"/></button>
+                </td>
+            </tr>
+        </tfoot>
+    </table>
+</form>
+    <c:forEach var="comment" items="${viewmodel.comments}">
+        <div class="comment">
+            <div class="caption">
+                <c:if test="${not empty comment.author}">
+                    <c:out value="${comment.author.displayname}"/>
+                </c:if>
+                <c:if test="${empty comment.author}">
+                    <fmt:message key="issue.comments.anonauthor"/>
+                </c:if>
+            </div>
+            <div class="smalltext">
+                <fmt:formatDate type="BOTH" value="${comment.created}" />
+                <c:if test="${comment.updateCount gt 0}">
+                    <!-- TODO: update count -->
+                </c:if>
+            </div>
+            <div class="medskip">
+                <c:out value="${comment.comment}"/>
+            </div>
+        </div>
+    </c:forEach>
+</c:if>
+
--- a/src/main/webapp/WEB-INF/jspf/issue-list.jspf	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/webapp/WEB-INF/jspf/issue-list.jspf	Fri Oct 23 13:29:33 2020 +0200
@@ -17,7 +17,7 @@
         <tr>
             <td>
                 <span class="phase-${issue.status.phase}">
-                    <a href="./projects/${issue.project.node}/issues/${issue.id}/edit">
+                    <a href="./projects/${issue.project.node}/issues/${issue.id}/view">
                         #${issue.id}&nbsp;-&nbsp;<c:out value="${issue.subject}" />
                     </a>
                 </span>
--- a/src/main/webapp/lightpit.css	Fri Oct 23 12:38:20 2020 +0200
+++ b/src/main/webapp/lightpit.css	Fri Oct 23 13:29:33 2020 +0200
@@ -145,12 +145,12 @@
     background: #f0f0ff;
 }
 
-button[type=submit] {
+button[type=submit], a.button.submit {
     background: #20a0ff;
     color: white;
 }
 
-button[type=submit]:hover {
+button[type=submit]:hover, a.button.submit:hover {
     background: #1090cf;
 }
 

mercurial