add convenience buttons for editing version, component, variant - resolves #733

Sat, 04 Oct 2025 13:34:33 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 04 Oct 2025 13:34:33 +0200
changeset 392
c0c7b4ca2946
parent 391
49f68aeb1dd2
child 393
0b5b37572021

add convenience buttons for editing version, component, variant - resolves #733

src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt file | annotate | diff | comparison | revisions
src/main/resources/localization/strings.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/strings_de.properties 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/component-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/project-details.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/variant-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/version-form.jsp file | annotate | diff | comparison | revisions
--- a/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt	Sat Oct 04 13:34:33 2025 +0200
@@ -138,8 +138,19 @@
         // set some internal request attributes
         val fullPath = req.servletPath + Optional.ofNullable(req.pathInfo).orElse("")
         req.setAttribute(Constants.REQ_ATTR_PATH, fullPath)
-        req.getHeader("Referer")?.let {
-            // TODO: add a sanity check to avoid link injection
+        req.getHeader("Referer")?.let { referer ->
+            val portInfo =
+                if ((req.scheme == "http" && req.serverPort == 80)
+                    || (req.scheme == "https" && req.serverPort == 443)
+                ) "" else ":${req.serverPort}"
+            val baseHrefOptionalPort = "${req.scheme}://${req.serverName}$portInfo${req.contextPath}/"
+            val baseHrefWithPort = "${req.scheme}://${req.serverName}${req.serverPort}${req.contextPath}/"
+            if (referer.startsWith(baseHrefOptionalPort) || referer.startsWith(baseHrefWithPort)) {
+                referer
+            } else {
+                null
+            }
+        }?.let {
             req.setAttribute(Constants.REQ_ATTR_REFERER, it)
         }
 
--- a/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt	Sat Oct 04 13:34:33 2025 +0200
@@ -125,6 +125,8 @@
             field = value
             if (value == null) {
                 request.removeAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION)
+            } else if (value.startsWith(baseHref)) {
+                request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, value)
             } else {
                 request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, baseHref + value)
             }
@@ -226,6 +228,19 @@
     }
 
     fun i18n(key: String): String = ResourceBundle.getBundle("localization/strings", response.locale).getString(key)
+
+    fun sanitizeReferer(referer: String?): String? {
+        if (referer == null) return null
+        // if someone really explicitly specifies the default port, we must support that, but we will remove it
+        val baseHrefWithPort = "${request.scheme}://${request.serverName}${request.serverPort}${request.contextPath}/"
+        return if (referer.startsWith(baseHref)) {
+            referer
+        } else if (referer.startsWith(baseHrefWithPort)) {
+            referer.replaceFirst(baseHrefWithPort, baseHref)
+        } else {
+            null
+        }
+    }
 }
 
 /**
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Sat Oct 04 13:34:33 2025 +0200
@@ -262,7 +262,7 @@
             dao.updateVersion(version)
         }
 
-        http.renderCommit("projects/${project.node}/versions/")
+        http.renderCommit(http.sanitizeReferer(http.param("returnLink")) ?: "projects/${project.node}/versions/")
     }
 
     private fun components(http: HttpRequest, dao: DataAccessObject) {
@@ -322,7 +322,7 @@
             dao.updateComponent(component)
         }
 
-        http.renderCommit("projects/${project.node}/components/")
+        http.renderCommit(http.sanitizeReferer(http.param("returnLink")) ?: "projects/${project.node}/components/")
     }
 
     private fun variants(http: HttpRequest, dao: DataAccessObject) {
@@ -378,7 +378,7 @@
             dao.updateVariant(variant)
         }
 
-        http.renderCommit("projects/${project.node}/variants/")
+        http.renderCommit(http.sanitizeReferer(http.param("returnLink")) ?: "projects/${project.node}/variants/")
     }
 
     private fun issue(http: HttpRequest, dao: DataAccessObject) {
--- a/src/main/resources/localization/strings.properties	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/resources/localization/strings.properties	Sat Oct 04 13:34:33 2025 +0200
@@ -46,6 +46,7 @@
 button.save=Save
 button.user.create=Add Developer
 button.variant.create=New Variant
+button.variant.edit=Edit Variant
 button.version.create=New Version
 button.version.edit=Edit Version
 button.whats-new=Show Changelog
--- a/src/main/resources/localization/strings_de.properties	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/resources/localization/strings_de.properties	Sat Oct 04 13:34:33 2025 +0200
@@ -46,6 +46,7 @@
 button.save=Speichern
 button.user.create=Neuer Entwickler
 button.variant.create=Neue Variante
+button.variant.edit=Variante Bearbeiten
 button.version.create=Neue Version
 button.version.edit=Version Bearbeiten
 button.whats-new=Versionshinweise \u00d6ffnen
@@ -65,7 +66,7 @@
 error.exceptionText = Interne Ausnahme
 error.headline = Die angeforderte Seite kann nicht angezeigt werden.
 error.message = Server Nachricht
-error.returnLink = Kehre zurück zu
+error.returnLink = Kehre zur\u00fcck zu
 error.timestamp = Zeitstempel
 feed.issues.description=Feed \u00fcber k\u00fcrzlich aktualisierte Vorg\u00e4nge.
 feed.issues.title=LightPIT Vorg\u00e4nge
@@ -161,7 +162,7 @@
 no-users=Bislang wurden keine Entwickler hinterlegt.
 node.tooltip=Name, der zur Konstruktion der URL genutzt werden soll.
 node=Pfadname
-ordinal.tooltip=Use to override lexicographic ordering. \u00dcbersteuert die lexikographische Sortierung.
+ordinal.tooltip=\u00dcbersteuert die lexikographische Sortierung.
 ordinal=Sequenznummer
 placeholder.auto-assignee.tooltip=Weist, wenn m\u00f6glich, den Vorgang dem Leiter der Komponente.
 placeholder.auto-assignee=Automatisch
--- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf	Sat Oct 04 13:34:33 2025 +0200
@@ -30,6 +30,7 @@
     <li>Pop-Up hinzugefügt, das über eine neue LightPIT-Version informiert.</li>
     <li>"Erledigt" Schaltfläche zur Vorgangsansicht hinzugefügt.</li>
     <li>"In Projekt Öffnen" Schaltfläche zur (globalen) Vorgangsansicht hinzugefügt.</li>
+    <li>Schaltflächen hinzugefügt, die schnelleren Zugang zu den Editoren für Versionen, Komponenten und Varianten bieten.</li>
     <li>Es können nun neue Vorgänge direkt mit einer Verknüpfung zu einem existierenden Vorgang erstellt werden.</li>
     <li>Neuen Filter "zeige nur nicht-blockierte" hinzugefügt.</li>
     <li>Vorgänge können nun auch direkt über die Vorgangsnummer (anstatt Raute + Nummer) verlinkt werden.</li>
--- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf	Sat Oct 04 13:34:33 2025 +0200
@@ -30,6 +30,7 @@
     <li>Add popup informing about a new LightPIT release.</li>
     <li>Add convenience RESOLVE button to the issue view.</li>
     <li>Add convenience OPEN IN PROJECT button to the global issue view.</li>
+    <li>Add buttons and hover-icons to quickly access the editor for versions, components, and variants.</li>
     <li>Add the possibility to create new related issues with one click.</li>
     <li>Add new filter "show only non-blocked".</li>
     <li>Change that you can now relate issues by just submitting their number (instead of hash + number).</li>
--- a/src/main/webapp/WEB-INF/jsp/component-form.jsp	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/webapp/WEB-INF/jsp/component-form.jsp	Sat Oct 04 13:34:33 2025 +0200
@@ -31,6 +31,10 @@
 <jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ComponentEditView" scope="request" />
 <c:set var="component" scope="page" value="${viewmodel.component}"/>
 <c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/>
+<c:set scope="page" var="returnLink" value="${requestScope[Constants.REQ_ATTR_REFERER]}"/>
+<c:if test="${empty returnLink}">
+    <c:set scope="page" var="returnLink" value="./projects/${project.node}/components/"/>
+</c:if>
 
 <form action="./projects/${project.node}/components/-/commit" method="post">
     <table class="formtable" style="width: 70ch">
@@ -94,7 +98,8 @@
         <tr>
             <td colspan="2">
                 <input type="hidden" name="id" value="${component.id}"/>
-                <a href="./projects/${project.node}/components/" class="button">
+                <input type="hidden" name="returnLink" value="${returnLink}"/>
+                <a href="${returnLink}" class="button">
                     <fmt:message key="button.cancel"/>
                 </a>
                 <button type="submit"><fmt:message key="button.okay"/></button>
--- a/src/main/webapp/WEB-INF/jsp/project-details.jsp	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp	Sat Oct 04 13:34:33 2025 +0200
@@ -38,6 +38,15 @@
 
 <div>
     <a href="${issuesHref}-/create" class="button"><fmt:message key="button.issue.create"/></a>
+    <c:if test="${not empty viewmodel.versionInfo}">
+        <a href="./projects/${project.node}/versions/${viewmodel.versionInfo.version.node}/edit" class="button"><fmt:message key="button.version.edit"/></a>
+    </c:if>
+    <c:if test="${not empty component}">
+        <a href="./projects/${project.node}/components/${component.node}/edit" class="button"><fmt:message key="button.component.edit"/></a>
+    </c:if>
+    <c:if test="${not empty variant}">
+        <a href="./projects/${project.node}/variants/${variant.node}/edit" class="button"><fmt:message key="button.variant.edit"/></a>
+    </c:if>
 </div>
 
 <h3><fmt:message key="issue.filter" /></h3>
--- a/src/main/webapp/WEB-INF/jsp/variant-form.jsp	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/webapp/WEB-INF/jsp/variant-form.jsp	Sat Oct 04 13:34:33 2025 +0200
@@ -29,8 +29,12 @@
 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 
 <jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.VariantEditView" scope="request" />
+<c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/>
 <c:set var="variant" scope="page" value="${viewmodel.variant}"/>
-<c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/>
+<c:set scope="page" var="returnLink" value="${requestScope[Constants.REQ_ATTR_REFERER]}"/>
+<c:if test="${empty returnLink}">
+    <c:set scope="page" var="returnLink" value="./projects/${project.node}/variants/"/>
+</c:if>
 
 <form action="./projects/${project.node}/variants/-/commit" method="post">
     <table class="formtable" style="width: 70ch">
@@ -81,7 +85,8 @@
         <tr>
             <td colspan="2">
                 <input type="hidden" name="id" value="${variant.id}"/>
-                <a href="./projects/${project.node}/variants/" class="button">
+                <input type="hidden" name="returnLink" value="${returnLink}"/>
+                <a href="${returnLink}" class="button">
                     <fmt:message key="button.cancel"/>
                 </a>
                 <button type="submit"><fmt:message key="button.okay"/></button>
--- a/src/main/webapp/WEB-INF/jsp/version-form.jsp	Mon Sep 22 20:00:59 2025 +0200
+++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp	Sat Oct 04 13:34:33 2025 +0200
@@ -31,6 +31,10 @@
 <jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.VersionEditView" scope="request" />
 <c:set var="version" scope="page" value="${viewmodel.version}"/>
 <c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/>
+<c:set scope="page" var="returnLink" value="${requestScope[Constants.REQ_ATTR_REFERER]}"/>
+<c:if test="${empty returnLink}">
+    <c:set scope="page" var="returnLink" value="./projects/${project.node}/versions/"/>
+</c:if>
 
 <form action="./projects/${project.node}/versions/-/commit" method="post">
     <table class="formtable" style="width: 35ch">
@@ -89,7 +93,8 @@
         <tr>
             <td colspan="2">
                 <input type="hidden" name="id" value="${version.id}"/>
-                <a href="./projects/${project.node}/versions/" class="button">
+                <input type="hidden" name="returnLink" value="${returnLink}"/>
+                <a href="${returnLink}" class="button">
                     <fmt:message key="button.cancel"/>
                 </a>
                 <button type="submit"><fmt:message key="button.okay"/></button>

mercurial