Sat, 04 Oct 2025 13:34:33 +0200
add convenience buttons for editing version, component, variant - resolves #733
--- 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>