Sat, 06 Sep 2025 14:22:38 +0200
add button for creating a new related issue
resolves #714
--- a/src/main/kotlin/de/uapcore/lightpit/Constants.kt Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/Constants.kt Sat Sep 06 14:22:38 2025 +0200 @@ -29,7 +29,7 @@ /** * A data in yyyy-mm-dd format to identify the release. */ - const val VERSION_DATE = "2025-09-05" + const val VERSION_DATE = "2025-09-06" /** * The path where the JSP files reside.
--- a/src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/logic/IssueLogic.kt Sat Sep 06 14:22:38 2025 +0200 @@ -8,10 +8,7 @@ import de.uapcore.lightpit.types.IssueStatus import de.uapcore.lightpit.types.IssueStatusPhase import de.uapcore.lightpit.types.RelationType -import de.uapcore.lightpit.viewmodel.IssueDetailView -import de.uapcore.lightpit.viewmodel.PathInfos -import de.uapcore.lightpit.viewmodel.PathInfosFull -import de.uapcore.lightpit.viewmodel.projectNavMenu +import de.uapcore.lightpit.viewmodel.* import java.sql.Date fun Issue.hasChanged(reference: Issue) = @@ -180,6 +177,19 @@ } } +fun determineRelationType(str: String?): Pair<RelationType, Boolean>? { + if (str.isNullOrBlank()) return null + return try { + if (str.startsWith("!")) { + Pair(RelationType.valueOf(str.substring(1)), true) + } else { + Pair(RelationType.valueOf(str), false) + } + } catch (_: IllegalArgumentException) { + null + } +} + fun addIssueRelation(http: HttpRequest, dao: DataAccessObject, pathInfos: PathInfos) { val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue) if (issue == null) { @@ -188,17 +198,7 @@ } // determine the relation type - val type: Pair<RelationType, Boolean>? = http.param("type")?.let { - try { - if (it.startsWith("!")) { - Pair(RelationType.valueOf(it.substring(1)), true) - } else { - Pair(RelationType.valueOf(it), false) - } - } catch (_: IllegalArgumentException) { - null - } - } + val type = determineRelationType(http.param("type")) // if the relation type was invalid, send HTTP 500 if (type == null) { @@ -206,6 +206,13 @@ return } + // check if a new issue should be created + if (http.param("issue").isNullOrBlank()) { + val redirectBase = if (pathInfos.inProject) pathInfos.issuesHref else PathInfosSimple(issue.project).issuesHref + http.response.sendRedirect("${http.baseHref}${redirectBase}-/create?r=${http.param("type")}:${issue.id}") + return + } + // determine the target issue val targetIssue: Issue? = http.param("issue")?.let { (if (it.startsWith("#") && it.length > 1) (it.substring(1).split(" ", limit = 2)[0]) else it)
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Sat Sep 06 14:22:38 2025 +0200 @@ -422,6 +422,7 @@ if (path.versionInfo is OptionalPathInfo.Specific) { if (path.versionInfo.elem.status.isReleased) { issue.affected = path.versionInfo.elem + // issues in past versions are most likely bugs issue.category = IssueCategory.Bug } else { issue.resolved = path.versionInfo.elem @@ -434,6 +435,24 @@ http.request.setAttribute("more", true) } + // test if we are auto-relating issues + val autoRelate = http.param("r") + if (autoRelate != null) { + val type = determineRelationType(autoRelate.split(":", limit=2)[0]) + // only relay sane query parameters + if (type != null) { + http.request.setAttribute("autoRelate", autoRelate) + // pick a useful category for the relation + if (type.first == RelationType.Tests && type.second) { + // tested by a Test + issue.category = IssueCategory.Test + } else if (type.first == RelationType.DefectOf && !type.second) { + // defect of a Bug + issue.category = IssueCategory.Bug + } + } + } + with(http) { pageTitle = (if (issue.id < 0) i18n("button.issue.create") else "#${issue.id} ${issue.subject}") + " (${issue.project.name})" @@ -481,6 +500,18 @@ issue.id } + // check if we shall relate the issue to something + val autoRelate = http.param("auto-relate") + if (autoRelate != null) { + val autoRelateInfo = autoRelate.split(":", limit = 2) + val type = determineRelationType(autoRelateInfo.getOrNull(0)) + val relatedIssue = autoRelateInfo.getOrNull(1)?.toIntOrNull()?.let(dao::findIssue) + // insert the relation when the parameter is sane, but do not report an invalid parameter + if (type != null && relatedIssue != null) { + dao.insertIssueRelation(IssueRelation(relatedIssue, Issue(openId, project), type.first, type.second)) + } + } + if (http.param("more") != null) { http.renderCommit("${issuesHref}-/create?more=true") } else if (http.param("save") != null) {
--- a/src/main/resources/localization/strings.properties Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/resources/localization/strings.properties Sat Sep 06 14:22:38 2025 +0200 @@ -103,6 +103,7 @@ issue.id=Issue ID issue.relations=Relations issue.relations.issue=Issue +issue.relations.target.placeholder=Search an issue or click Add to create a new one. issue.relations.target.invalid=Target issue cannot be linked. issue.relations.type=Type issue.relations.type.RelatesTo=relates to @@ -117,6 +118,8 @@ issue.relations.type.Blocks.rev=blocked by issue.relations.type.Tests=tests issue.relations.type.Tests.rev=tested by +issue.relations.type.DefectOf=defect of +issue.relations.type.DefectOf.rev=defect issue.relations.type.Duplicates=duplicates issue.relations.type.Duplicates.rev=duplicated by issue.resolved-versions=Target @@ -199,8 +202,6 @@ version.status=Status version=Version whats-new.info = A new version of LightPIT has been released. Do you want to check the release notes? -issue.relations.type.DefectOf=defect of -issue.relations.type.DefectOf.rev=defect issue.filter.sort.primary=Order by issue.filter.sort.secondary=then by issue.filter.sort.tertiary=then by
--- a/src/main/resources/localization/strings_de.properties Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/resources/localization/strings_de.properties Sat Sep 06 14:22:38 2025 +0200 @@ -103,6 +103,7 @@ issue.id=Vorgangs-ID issue.relations=Beziehungen issue.relations.issue=Vorgang +issue.relations.target.placeholder=Vorgang suchen oder einen neuen Hinzuf\u00fcgen. issue.relations.target.invalid=Vorgang kann nicht verkn\u00fcpft werden. issue.relations.type=Typ issue.relations.type.RelatesTo=verwandt mit @@ -117,6 +118,8 @@ issue.relations.type.Blocks.rev=blockiert von issue.relations.type.Tests=testet issue.relations.type.Tests.rev=getestet durch +issue.relations.type.DefectOf=Fehler von +issue.relations.type.DefectOf.rev=Fehler issue.relations.type.Duplicates=Duplikat von issue.relations.type.Duplicates.rev=Duplikat issue.resolved-versions=Ziel @@ -199,8 +202,6 @@ version.status=Status version=Version whats-new.info = Eine neue LightPIT-Version wurde ver\u00f6ffentlicht. Wollen Sie mehr erfahren? -issue.relations.type.DefectOf.rev=Fehler -issue.relations.type.DefectOf=Fehler von issue.filter.sort.primary=Sortiere nach issue.filter.sort.secondary=dann nach issue.filter.sort.tertiary=dann nach
--- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Sat Sep 06 14:22:38 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>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> <li>Die Standardkategorie für neue Vorgänge in veröffentlichten Versionen ist nun "Fehler" anstelle von "Feature".</li>
--- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf Sat Sep 06 14:22:38 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 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> <li>Change that the default category for new issues in released versions is Bug instead of Feature.</li>
--- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Sep 06 14:22:38 2025 +0200 @@ -201,6 +201,7 @@ <tr> <td colspan="2"> <input type="hidden" name="id" value="${issue.id}"/> + <input type="hidden" name="auto-relate" value="${autoRelate}" /> <c:if test="${not empty viewmodel.pathInfos}"> <input type="checkbox" id="more" name="more" <c:if test="${more}">checked</c:if> /> <label for="more"><fmt:message key="button.issue.create.another"/> </label>
--- a/src/main/webapp/WEB-INF/jsp/issue-view.jsp Fri Sep 05 22:09:08 2025 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issue-view.jsp Sat Sep 06 14:22:38 2025 +0200 @@ -263,7 +263,8 @@ </select> </td> <td> - <input id="linkable-issues" data-project="${issue.project.id}" name="issue" list="linkable-issues-list" autocomplete="off"> + <input id="linkable-issues" data-project="${issue.project.id}" name="issue" + list="linkable-issues-list" autocomplete="off" placeholder="<fmt:message key="issue.relations.target.placeholder"/>"> <datalist id="linkable-issues-list"></datalist> </td> </tr>