Sat, 30 May 2020 18:05:06 +0200
adds version selection in issue editor
1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 30 15:28:27 2020 +0200 1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 30 18:05:06 2020 +0200 1.3 @@ -39,10 +39,7 @@ 1.4 import javax.servlet.http.HttpServletResponse; 1.5 import javax.servlet.http.HttpSession; 1.6 import java.io.IOException; 1.7 -import java.lang.reflect.Constructor; 1.8 -import java.lang.reflect.InvocationTargetException; 1.9 -import java.lang.reflect.Method; 1.10 -import java.lang.reflect.Modifier; 1.11 +import java.lang.reflect.*; 1.12 import java.sql.Connection; 1.13 import java.sql.SQLException; 1.14 import java.util.*; 1.15 @@ -280,30 +277,44 @@ 1.16 * @return the parameter value or an empty optional, if no parameter with the specified name was found 1.17 */ 1.18 protected <T> Optional<T> getParameter(HttpServletRequest req, Class<T> clazz, String name) { 1.19 - final String paramValue = req.getParameter(name); 1.20 - if (paramValue == null) return Optional.empty(); 1.21 - if (clazz.equals(Boolean.class)) { 1.22 - if (paramValue.toLowerCase().equals("false") || paramValue.equals("0")) { 1.23 - return Optional.of((T)Boolean.FALSE); 1.24 - } else { 1.25 - return Optional.of((T)Boolean.TRUE); 1.26 + if (clazz.isArray()) { 1.27 + final String[] paramValues = req.getParameterValues(name); 1.28 + int len = paramValues == null ? 0 : paramValues.length; 1.29 + final var array = (T) Array.newInstance(clazz.getComponentType(), len); 1.30 + for (int i = 0 ; i < len ; i++) { 1.31 + try { 1.32 + final Constructor<?> ctor = clazz.getComponentType().getConstructor(String.class); 1.33 + Array.set(array, i, ctor.newInstance(paramValues[i])); 1.34 + } catch (ReflectiveOperationException e) { 1.35 + throw new RuntimeException(e); 1.36 + } 1.37 + } 1.38 + return Optional.of(array); 1.39 + } else { 1.40 + final String paramValue = req.getParameter(name); 1.41 + if (paramValue == null) return Optional.empty(); 1.42 + if (clazz.equals(Boolean.class)) { 1.43 + if (paramValue.toLowerCase().equals("false") || paramValue.equals("0")) { 1.44 + return Optional.of((T) Boolean.FALSE); 1.45 + } else { 1.46 + return Optional.of((T) Boolean.TRUE); 1.47 + } 1.48 + } 1.49 + if (clazz.equals(String.class)) return Optional.of((T) paramValue); 1.50 + if (java.sql.Date.class.isAssignableFrom(clazz)) { 1.51 + try { 1.52 + return Optional.of((T) java.sql.Date.valueOf(paramValue)); 1.53 + } catch (IllegalArgumentException ex) { 1.54 + return Optional.empty(); 1.55 + } 1.56 + } 1.57 + try { 1.58 + final Constructor<T> ctor = clazz.getConstructor(String.class); 1.59 + return Optional.of(ctor.newInstance(paramValue)); 1.60 + } catch (ReflectiveOperationException e) { 1.61 + throw new RuntimeException(e); 1.62 } 1.63 } 1.64 - if (clazz.equals(String.class)) return Optional.of((T) paramValue); 1.65 - if (java.sql.Date.class.isAssignableFrom(clazz)) { 1.66 - try { 1.67 - return Optional.of((T)java.sql.Date.valueOf(paramValue)); 1.68 - } catch (IllegalArgumentException ex) { 1.69 - return Optional.empty(); 1.70 - } 1.71 - } 1.72 - try { 1.73 - final Constructor<T> ctor = clazz.getConstructor(String.class); 1.74 - return Optional.of(ctor.newInstance(paramValue)); 1.75 - } catch (ReflectiveOperationException e) { 1.76 - throw new RuntimeException(e); 1.77 - } 1.78 - 1.79 } 1.80 1.81 /**
2.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat May 30 15:28:27 2020 +0200 2.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat May 30 18:05:06 2020 +0200 2.3 @@ -43,22 +43,27 @@ 2.4 2.5 public final class PGIssueDao implements IssueDao { 2.6 2.7 - private final PreparedStatement insert, update, list, find, affectedVersions, scheduledVersions, resolvedVersions; 2.8 + private final PreparedStatement insert, update, list, find; 2.9 + private final PreparedStatement affectedVersions, scheduledVersions, resolvedVersions; 2.10 + private final PreparedStatement clearAffected, clearScheduled, clearResolved; 2.11 + private final PreparedStatement insertAffected, insertScheduled, insertResolved; 2.12 2.13 public PGIssueDao(Connection connection) throws SQLException { 2.14 list = connection.prepareStatement( 2.15 - "select issueid, project, status, category, subject, description, " + 2.16 + "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 2.17 "userid, username, givenname, lastname, mail, " + 2.18 "created, updated, eta " + 2.19 - "from lpit_issue " + 2.20 + "from lpit_issue i " + 2.21 + "left join lpit_project p on project = projectid " + 2.22 "left join lpit_user on userid = assignee " + 2.23 "where project = ? "); 2.24 2.25 find = connection.prepareStatement( 2.26 - "select issueid, project, status, category, subject, description, " + 2.27 + "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 2.28 "userid, username, givenname, lastname, mail, " + 2.29 "created, updated, eta " + 2.30 - "from lpit_issue " + 2.31 + "from lpit_issue i " + 2.32 + "left join lpit_project p on project = projectid " + 2.33 "left join lpit_user on userid = assignee " + 2.34 "where issueid = ? "); 2.35 2.36 @@ -72,25 +77,31 @@ 2.37 ); 2.38 2.39 affectedVersions = connection.prepareStatement( 2.40 - "select v.versionid, v.name, v.status, v.ordinal " + 2.41 - "from lpit_version v join lpit_issue_affected_version using (versionid) " + 2.42 + "select versionid, name, status, ordinal " + 2.43 + "from lpit_version join lpit_issue_affected_version using (versionid) " + 2.44 "where issueid = ? " + 2.45 - "order by v.ordinal, v.name" 2.46 + "order by ordinal, name" 2.47 ); 2.48 + clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?"); 2.49 + insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)"); 2.50 2.51 scheduledVersions = connection.prepareStatement( 2.52 - "select v.versionid, v.name, v.status, v.ordinal " + 2.53 - "from lpit_version v join lpit_issue_scheduled_version using (versionid) " + 2.54 + "select versionid, name, status, ordinal " + 2.55 + "from lpit_version join lpit_issue_scheduled_version using (versionid) " + 2.56 "where issueid = ? " + 2.57 - "order by v.ordinal, v.name" 2.58 + "order by ordinal, name" 2.59 ); 2.60 + clearScheduled = connection.prepareStatement("delete from lpit_issue_scheduled_version where issueid = ?"); 2.61 + insertScheduled = connection.prepareStatement("insert into lpit_issue_scheduled_version (issueid, versionid) values (?,?)"); 2.62 2.63 resolvedVersions = connection.prepareStatement( 2.64 - "select v.versionid, v.name, v.status, v.ordinal " + 2.65 + "select versionid, name, status, ordinal " + 2.66 "from lpit_version v join lpit_issue_resolved_version using (versionid) " + 2.67 "where issueid = ? " + 2.68 - "order by v.ordinal, v.name" 2.69 + "order by ordinal, name" 2.70 ); 2.71 + clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?"); 2.72 + insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)"); 2.73 } 2.74 2.75 private User obtainAssignee(ResultSet result) throws SQLException { 2.76 @@ -109,6 +120,7 @@ 2.77 2.78 private Issue mapColumns(ResultSet result) throws SQLException { 2.79 final var project = new Project(result.getInt("project")); 2.80 + project.setName(result.getString("projectname")); 2.81 final var issue = new Issue(result.getInt("issueid"), project); 2.82 issue.setStatus(IssueStatus.valueOf(result.getString("status"))); 2.83 issue.setCategory(IssueCategory.valueOf(result.getString("category"))); 2.84 @@ -122,13 +134,37 @@ 2.85 } 2.86 2.87 private Version mapVersion(ResultSet result, Project project) throws SQLException { 2.88 - final var version = new Version(result.getInt("v.versionid"), project); 2.89 - version.setName(result.getString("v.name")); 2.90 - version.setOrdinal(result.getInt("v.ordinal")); 2.91 - version.setStatus(VersionStatus.valueOf(result.getString("v.status"))); 2.92 + final var version = new Version(result.getInt("versionid"), project); 2.93 + version.setName(result.getString("name")); 2.94 + version.setOrdinal(result.getInt("ordinal")); 2.95 + version.setStatus(VersionStatus.valueOf(result.getString("status"))); 2.96 return version; 2.97 } 2.98 2.99 + private void updateVersionLists(Issue instance) throws SQLException { 2.100 + clearAffected.setInt(1, instance.getId()); 2.101 + clearScheduled.setInt(1, instance.getId()); 2.102 + clearResolved.setInt(1, instance.getId()); 2.103 + insertAffected.setInt(1, instance.getId()); 2.104 + insertScheduled.setInt(1, instance.getId()); 2.105 + insertResolved.setInt(1, instance.getId()); 2.106 + clearAffected.executeUpdate(); 2.107 + clearScheduled.executeUpdate(); 2.108 + clearResolved.executeUpdate(); 2.109 + for (Version v : instance.getAffectedVersions()) { 2.110 + insertAffected.setInt(2, v.getId()); 2.111 + insertAffected.executeUpdate(); 2.112 + } 2.113 + for (Version v : instance.getScheduledVersions()) { 2.114 + insertScheduled.setInt(2, v.getId()); 2.115 + insertScheduled.executeUpdate(); 2.116 + } 2.117 + for (Version v : instance.getResolvedVersions()) { 2.118 + insertResolved.setInt(2, v.getId()); 2.119 + insertResolved.executeUpdate(); 2.120 + } 2.121 + } 2.122 + 2.123 @Override 2.124 public void save(Issue instance) throws SQLException { 2.125 Objects.requireNonNull(instance.getSubject()); 2.126 @@ -144,6 +180,7 @@ 2.127 final var rs = insert.executeQuery(); 2.128 rs.next(); 2.129 instance.setId(rs.getInt(1)); 2.130 + updateVersionLists(instance); 2.131 } 2.132 2.133 @Override 2.134 @@ -157,7 +194,13 @@ 2.135 setForeignKeyOrNull(update, 5, instance.getAssignee(), User::getId); 2.136 setDateOrNull(update, 6, instance.getEta()); 2.137 update.setInt(7, instance.getId()); 2.138 - return update.executeUpdate() > 0; 2.139 + boolean success = update.executeUpdate() > 0; 2.140 + if (success) { 2.141 + updateVersionLists(instance); 2.142 + return true; 2.143 + } else { 2.144 + return false; 2.145 + } 2.146 } 2.147 2.148 @Override
3.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Sat May 30 15:28:27 2020 +0200 3.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Sat May 30 18:05:06 2020 +0200 3.3 @@ -46,14 +46,16 @@ 3.4 3.5 public PGVersionDao(Connection connection) throws SQLException { 3.6 list = connection.prepareStatement( 3.7 - "select versionid, project, name, ordinal, status " + 3.8 - "from lpit_version " + 3.9 + "select versionid, project, p.name as projectname, v.name, ordinal, status " + 3.10 + "from lpit_version v " + 3.11 + "join lpit_project p on v.project = p.projectid " + 3.12 "where project = ? " + 3.13 - "order by ordinal desc, lower(name) desc"); 3.14 + "order by ordinal desc, lower(v.name) desc"); 3.15 3.16 find = connection.prepareStatement( 3.17 - "select versionid, project, name, ordinal, status " + 3.18 - "from lpit_version " + 3.19 + "select versionid, project, p.name as projectname, v.name, ordinal, status " + 3.20 + "from lpit_version v " + 3.21 + "join lpit_project p on v.project = p.projectid " + 3.22 "where versionid = ?"); 3.23 3.24 insert = connection.prepareStatement( 3.25 @@ -88,6 +90,7 @@ 3.26 3.27 private Version mapColumns(ResultSet result) throws SQLException { 3.28 final var project = new Project(result.getInt("project")); 3.29 + project.setName(result.getString("projectname")); 3.30 final var version = new Version(result.getInt("versionid"), project); 3.31 version.setName(result.getString("name")); 3.32 version.setOrdinal(result.getInt("ordinal"));
4.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 30 15:28:27 2020 +0200 4.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 30 18:05:06 2020 +0200 4.3 @@ -42,10 +42,9 @@ 4.4 import java.io.IOException; 4.5 import java.sql.Date; 4.6 import java.sql.SQLException; 4.7 -import java.util.ArrayList; 4.8 -import java.util.List; 4.9 -import java.util.NoSuchElementException; 4.10 -import java.util.Objects; 4.11 +import java.util.*; 4.12 +import java.util.stream.Collectors; 4.13 +import java.util.stream.Stream; 4.14 4.15 import static de.uapcore.lightpit.Functions.fqn; 4.16 4.17 @@ -109,14 +108,14 @@ 4.18 } 4.19 4.20 void selectVersion(Version version) { 4.21 - if (!Objects.equals(project, version.getProject())) throw new AssertionError("Nice, you implemented a bug!"); 4.22 + this.project = version.getProject(); 4.23 this.version = version; 4.24 this.issue = null; 4.25 updateAttributes(); 4.26 } 4.27 4.28 void selectIssue(Issue issue) { 4.29 - if (!Objects.equals(issue.getProject(), project)) throw new AssertionError("Nice, you implemented a bug!"); 4.30 + this.project = issue.getProject(); 4.31 this.issue = issue; 4.32 this.version = null; 4.33 updateAttributes(); 4.34 @@ -375,7 +374,16 @@ 4.35 } 4.36 4.37 private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 4.38 - req.setAttribute("projects", dao.getProjectDao().list()); 4.39 + 4.40 + if (selection.issue.getProject() == null || selection.issue.getProject().getId() < 0) { 4.41 + req.setAttribute("projects", dao.getProjectDao().list()); 4.42 + req.setAttribute("versions", Collections.<Version>emptyList()); 4.43 + } else { 4.44 + req.setAttribute("projects", Collections.<Project>emptyList()); 4.45 + req.setAttribute("versions", dao.getVersionDao().list(selection.issue.getProject())); 4.46 + } 4.47 + 4.48 + dao.getIssueDao().joinVersionInformation(selection.issue); 4.49 req.setAttribute("issue", selection.issue); 4.50 req.setAttribute("issueStatusEnum", IssueStatus.values()); 4.51 req.setAttribute("issueCategoryEnum", IssueCategory.values()); 4.52 @@ -428,6 +436,23 @@ 4.53 ).ifPresent(issue::setAssignee); 4.54 getParameter(req, String.class, "description").ifPresent(issue::setDescription); 4.55 getParameter(req, Date.class, "eta").ifPresent(issue::setEta); 4.56 + 4.57 + getParameter(req, Integer[].class, "affected") 4.58 + .map(Stream::of) 4.59 + .map(stream -> 4.60 + stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 4.61 + ).ifPresent(issue::setAffectedVersions); 4.62 + getParameter(req, Integer[].class, "scheduled") 4.63 + .map(Stream::of) 4.64 + .map(stream -> 4.65 + stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 4.66 + ).ifPresent(issue::setScheduledVersions); 4.67 + getParameter(req, Integer[].class, "resolved") 4.68 + .map(Stream::of) 4.69 + .map(stream -> 4.70 + stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 4.71 + ).ifPresent(issue::setResolvedVersions); 4.72 + 4.73 dao.getIssueDao().saveOrUpdate(issue); 4.74 4.75 // specifying the issue parameter keeps the edited issue as breadcrumb
5.1 --- a/src/main/resources/localization/projects.properties Sat May 30 15:28:27 2020 +0200 5.2 +++ b/src/main/resources/localization/projects.properties Sat May 30 18:05:06 2020 +0200 5.3 @@ -46,7 +46,7 @@ 5.4 version.project=Project 5.5 version.name=Version 5.6 version.status=Status 5.7 -version.ordinal=Custom Ordering 5.8 +version.ordinal=Ordering 5.9 tooltip.ordinal=Use to override lexicographic ordering. 5.10 5.11 placeholder.null-owner=Unassigned 5.12 @@ -68,11 +68,8 @@ 5.13 issue.subject=Subject 5.14 issue.description=Description 5.15 issue.assignee=Assignee 5.16 -issue.affected-version=Affected Version 5.17 issue.affected-versions=Affected Versions 5.18 -issue.scheduled-version=Scheduled for Version 5.19 issue.scheduled-versions=Scheduled for Versions 5.20 -issue.resolved-version=Resolved in Version 5.21 issue.resolved-versions=Resolved in Versions 5.22 issue.category=Category 5.23 issue.status=Status
6.1 --- a/src/main/resources/localization/projects_de.properties Sat May 30 15:28:27 2020 +0200 6.2 +++ b/src/main/resources/localization/projects_de.properties Sat May 30 18:05:06 2020 +0200 6.3 @@ -68,11 +68,8 @@ 6.4 issue.subject=Thema 6.5 issue.description=Beschreibung 6.6 issue.assignee=Zugewiesen 6.7 -issue.affected-version=Betroffene Version 6.8 issue.affected-versions=Betroffene Versionen 6.9 -issue.scheduled-version=Zielversion 6.10 issue.scheduled-versions=Zielversionen 6.11 -issue.resolved-version=Gel\u00f6st in Version 6.12 issue.resolved-versions=Gel\u00f6st in Versionen 6.13 issue.category=Kategorie 6.14 issue.status=Status
7.1 --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat May 30 15:28:27 2020 +0200 7.2 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat May 30 18:05:06 2020 +0200 7.3 @@ -29,6 +29,7 @@ 7.4 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 7.5 7.6 <jsp:useBean id="projects" type="java.util.List<de.uapcore.lightpit.entities.Project>" scope="request" /> 7.7 +<jsp:useBean id="versions" type="java.util.List<de.uapcore.lightpit.entities.Version>" scope="request" /> 7.8 <jsp:useBean id="issue" type="de.uapcore.lightpit.entities.Issue" scope="request"/> 7.9 <jsp:useBean id="issueStatusEnum" type="de.uapcore.lightpit.entities.IssueStatus[]" scope="request"/> 7.10 <jsp:useBean id="issueCategoryEnum" type="de.uapcore.lightpit.entities.IssueCategory[]" scope="request"/> 7.11 @@ -44,13 +45,19 @@ 7.12 <tr> 7.13 <th><fmt:message key="issue.project"/></th> 7.14 <td> 7.15 + <c:if test="${issue.project.id ge 0}"> 7.16 + <c:out value="${issue.project.name}" /> 7.17 + <input type="hidden" name="pid" value="${issue.project.id}" /> 7.18 + </c:if> 7.19 + <c:if test="${empty issue.project or issue.project.id lt 0}"> 7.20 <select name="pid" required> 7.21 <c:forEach var="project" items="${projects}"> 7.22 - <option value="${project.id}" <c:if test="${project eq issue.project}">selected</c:if> > 7.23 + <option value="${project.id}"> 7.24 <c:out value="${project.name}" /> 7.25 </option> 7.26 </c:forEach> 7.27 </select> 7.28 + </c:if> 7.29 </td> 7.30 </tr> 7.31 <tr> 7.32 @@ -104,45 +111,32 @@ 7.33 </select> 7.34 </td> 7.35 </tr> 7.36 + <c:if test="${issue.project.id ge 0}"> 7.37 <tr> 7.38 - <th> 7.39 - <c:choose> 7.40 - <c:when test="${issue.affectedVersions.size() gt 1}"> 7.41 - <fmt:message key="issue.affected-versions"/> 7.42 - </c:when> 7.43 - <c:otherwise> 7.44 - <fmt:message key="issue.affected-version"/> 7.45 - </c:otherwise> 7.46 - </c:choose> 7.47 - </th> 7.48 - <td>TODO</td> 7.49 + <th class="vtop"><fmt:message key="issue.affected-versions"/></th> 7.50 + <td> 7.51 + <c:set var="fieldname" value="affected"/> 7.52 + <c:set var="data" value="${issue.affectedVersions}" /> 7.53 + <%@include file="../jspf/version-list.jsp"%> 7.54 + </td> 7.55 </tr> 7.56 <tr> 7.57 - <th> 7.58 - <c:choose> 7.59 - <c:when test="${issue.scheduledVersions.size() gt 1}"> 7.60 - <fmt:message key="issue.scheduled-versions"/> 7.61 - </c:when> 7.62 - <c:otherwise> 7.63 - <fmt:message key="issue.scheduled-version"/> 7.64 - </c:otherwise> 7.65 - </c:choose> 7.66 - </th> 7.67 - <td>TODO</td> 7.68 + <th class="vtop"><fmt:message key="issue.scheduled-versions"/></th> 7.69 + <td> 7.70 + <c:set var="fieldname" value="scheduled"/> 7.71 + <c:set var="data" value="${issue.scheduledVersions}" /> 7.72 + <%@include file="../jspf/version-list.jsp"%> 7.73 + </td> 7.74 </tr> 7.75 <tr> 7.76 - <th> 7.77 - <c:choose> 7.78 - <c:when test="${issue.resolvedVersions.size() gt 1}"> 7.79 - <fmt:message key="issue.resolved-versions"/> 7.80 - </c:when> 7.81 - <c:otherwise> 7.82 - <fmt:message key="issue.resolved-version"/> 7.83 - </c:otherwise> 7.84 - </c:choose> 7.85 - </th> 7.86 - <td>TODO</td> 7.87 + <th class="vtop"><fmt:message key="issue.resolved-versions"/></th> 7.88 + <td> 7.89 + <c:set var="fieldname" value="resolved"/> 7.90 + <c:set var="data" value="${issue.resolvedVersions}" /> 7.91 + <%@include file="../jspf/version-list.jsp"%> 7.92 + </td> 7.93 </tr> 7.94 + </c:if> 7.95 <tr> 7.96 <th><fmt:message key="issue.eta"/></th> 7.97 <td><input name="eta" type="date" value="<fmt:formatDate value="${issue.eta}" pattern="YYYY-MM-dd" />" /> </td>
8.1 --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat May 30 15:28:27 2020 +0200 8.2 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat May 30 18:05:06 2020 +0200 8.3 @@ -47,13 +47,19 @@ 8.4 <tr> 8.5 <th><fmt:message key="version.project"/></th> 8.6 <td> 8.7 - <select name="pid" required> 8.8 - <c:forEach var="project" items="${projects}"> 8.9 - <option value="${project.id}" <c:if test="${project eq version.project}">selected</c:if> > 8.10 - <c:out value="${project.name}" /> 8.11 - </option> 8.12 - </c:forEach> 8.13 - </select> 8.14 + <c:if test="${version.project.id ge 0}"> 8.15 + <c:out value="${version.project.name}" /> 8.16 + <input type="hidden" name="pid" value="${version.project.id}" /> 8.17 + </c:if> 8.18 + <c:if test="${empty version.project or version.project.id lt 0}"> 8.19 + <select name="pid" required> 8.20 + <c:forEach var="project" items="${projects}"> 8.21 + <option value="${project.id}"> 8.22 + <c:out value="${project.name}" /> 8.23 + </option> 8.24 + </c:forEach> 8.25 + </select> 8.26 + </c:if> 8.27 </td> 8.28 </tr> 8.29 <tr>
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/main/webapp/WEB-INF/jspf/version-list.jsp Sat May 30 18:05:06 2020 +0200 9.3 @@ -0,0 +1,13 @@ 9.4 +<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 9.5 + 9.6 +<select name="${fieldname}" multiple> 9.7 + <c:forEach var="version" items="${versions}"> 9.8 + <option value="${version.id}" 9.9 + <c:forEach var="v" items="${data}"> 9.10 + <c:if test="${v eq version}">selected</c:if> 9.11 + </c:forEach> 9.12 + > 9.13 + <c:out value="${version.name}" /> 9.14 + </option> 9.15 + </c:forEach> 9.16 +</select>